Skip to content

Commit c33d5f5

Browse files
bgavrilMStrwalke
andauthored
Cache key extensibility spec proposal (#5091)
* Cache key extensibility * Fix JSON syntax in cache_extensibility.md * Fix JSON formatting in cache_extensibility.md 1 * Update docs/cache_extensibility.md Co-authored-by: Travis Walker <[email protected]> * Omit secret value in cache documentation --------- Co-authored-by: Travis Walker <[email protected]>
1 parent 7116a7d commit c33d5f5

File tree

1 file changed

+125
-0
lines changed

1 file changed

+125
-0
lines changed

docs/cache_extensibility.md

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# MSAL Cache Key Extensibility Proposal
2+
3+
This document looks at defining a strategy for extending how tokens are cached internally by MSAL, allowing app developers to cache multiple tokens by a custom key.
4+
5+
## Functional Requirements
6+
7+
- Allow higher level SDKs to extend the default cache key semantics that all MSALs come with.
8+
- Non-breaking changes
9+
- Implementations MUST be consistent across confidential client MSALs
10+
- `AcquireTokenForClient` (client_credentials) MUST support this mechanism. Other confidential client flows (web app, web api, ROPC) SHOULD support it.
11+
12+
## Non-Functional Requirements
13+
14+
- Performance of cache look-up operations does not degrade.
15+
- Forward compatibility strategy - older MSALs must continue to work on the with the same cache as the new ones, to support upgrade scenarios. If this is not possible, an intermediate version of MSAL must be released that supports this.
16+
- Logging must be extended so that MSAL developers can understand a cache miss.
17+
18+
### Example scenarios that will use this extensibility point
19+
20+
- Associate tokens with the client certificate used to obtain them.
21+
- Associtate tokens with the client secrets used to obtain them.
22+
- Allow an application to associate tokens with a SPIFEE type of identifier which is known upfront by the clients.
23+
24+
### Prior art
25+
26+
The cache key schema is defined [here](https://identitydivision.visualstudio.com/DevEx/_git/AuthLibrariesApiReview?path=/SSO/Schema.md) and extended for POP tokens [here](https://identitydivision.visualstudio.com/DevEx/_git/AuthLibrariesApiReview?path=/SSO/change_proposals/11232019-accesstoken_with_authscheme.md). A related cache extensiblity enhancement has been done [here](https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/4922).
27+
28+
## Developer Experience
29+
30+
#### MSAL
31+
32+
```csharp
33+
34+
var app = ConfidentialClientApplicationBuilder.Create("client_id")
35+
.WithClientCertifiacte(x509Cert)
36+
.WithExperimentalFeatures(true) // All extensiblity APIs remain experimental
37+
.BuildConcrete();
38+
39+
var result = await app.AcquireTokenForClient("https://graph.microsoft.com/.default")
40+
.WithAdditionalCacheKeyComponents(new Dictionary<string, string>{ // New API
41+
{"cert_thubprint", x509Cert.GetSha2THumbprint() },
42+
{"spiffee_id", "37" }
43+
});
44+
.ExecuteAsync();
45+
```
46+
47+
After executing the AcquireToken instruction, MSAL shall associate the token with the existing components - authority, scope and client_id. In addition, it will assocaite the token with "cert_thumbprint" and "spifee_id".
48+
49+
#### Higher level APIs
50+
51+
Do not expose this logic in higher level APIs.
52+
53+
## Token Schema changes
54+
55+
### Cache Key
56+
57+
Similar to the [POP](https://identitydivision.visualstudio.com/DevEx/_git/AuthLibrariesApiReview?path=/SSO/change_proposals/11232019-accesstoken_with_authscheme.md&_a=preview) enhancement, a new credential type will be used - `atext`.
58+
59+
The access token cache key will also add a suffix composed as follows (all operations are ordinal case sensitive):
60+
61+
1. Take the key-value pair list of components and **order** it alphabetically by the key (e.g. "key1": "val1", "key2": "val2")
62+
1. Concatenate this list (e.g. "key1val1key2val2")
63+
1. Hash this using SHA256 - (e.g. `cc252f65706f969930208e4b2403435a95f7f7d9c964bd190ae2c6e032938235`)
64+
65+
So for the token in the example above the cache entry will be:
66+
67+
`-login.microsoftonline.com-atext-client_id-tenant_id-https://graph.microsoft.com/.deafult-cc252f65706f969930208e4b2403435a95f7f7d9c964bd190ae2c6e032938235`
68+
69+
### Cache payload
70+
71+
Each key value pair must be included in the cache payload. Avoid collisions with existing documented keys.
72+
73+
Example:
74+
75+
```json
76+
{
77+
"AccessToken": {
78+
"-login.microsoftonline.com-atext-client_id-tenant_id-https://graph.microsoft.com/.deafult-cc252f65706f969930208e4b2403435a95f7f7d9c964bd190ae2c6e032938235": {
79+
"home_account_id": "6afc833f-49c0-4fd5-b685-2998a6cc8d8d.469fdeb4-d4fd-4fde-991e-308a78e4bea4",
80+
"environment": "login.microsoftonline.de",
81+
"client_id": "0615b6ca-88d4-4884-8729-b178178f7c27",
82+
"secret": "omitted",
83+
"credential_type": "atext", // new!
84+
"realm": "469fdeb4-d4fd-4fde-991e-308a78e4bea4",
85+
"target": "https://graph.cloudapi.de/62e90394-69f5-4237-9190-012177145e10 https://graph.cloudapi.de/.default",
86+
"cached_at": "1553819803",
87+
"expires_on": "1553823402",
88+
"key1": "val1", // new!
89+
"key2": "val2", // new! }
90+
}
91+
}
92+
```
93+
94+
Note: in case the values contain reserved JSON characters, use standard JSON escape rules to serialize.
95+
96+
### Cache lookup logic
97+
98+
Both the hash value and the actual payload values must be taken into account when performing cache lookups.
99+
100+
### External cache key
101+
102+
MSALs which suggest a distributed cache key must include this differentiator in the cache key
103+
104+
### Interop with POP key
105+
106+
Leave the keyID logic as is. We can come back to this.
107+
108+
### User token cache key
109+
110+
In user flows, only the access tokens will be cached by the new schema. Refresh tokens and Id tokens remain shared. For simplicity, do **not** extend this API to user flows until a few scenarios crop up!
111+
112+
## Acceptance tests
113+
114+
1. The following tests assume a call to `AcquireTokenForClient` with `WithAdditionalCacheKeyComponents` set to "key1"="val1", "key2"="val2". Always assert suggested cache key.
115+
116+
- Call `AcquireTokenForClient` with the same "key1"="val1", "key2"="val2". Assert cache hit.
117+
- Call `AcquireTokenForClient` with no extensibility. Assert cache miss.
118+
- Call `AcquireTokenForClient` with the same "key1"="val1". Assert cache miss.
119+
- Call `AcquireTokenForClient` with the same "key1"="val1", "key2"="foo". Assert cache miss.
120+
- Call `AcquireTokenForClient` with the same "Key1"="val1", "key2"="val2". Assert cache miss (capital "K" used in "key1")
121+
122+
2. Forwards compatibility test: old MSAL must function side by side with new MSAL, i.e. old MSAL must ignore the new access token cache entries.
123+
124+
3. Try to use `WithAdditionalCacheKeyComponents` set to `client_id` -> error
125+
4. POP Access token (all known schemes) and WithAdditionalCacheKeyComponents. Assert external cache key.

0 commit comments

Comments
 (0)