-
Notifications
You must be signed in to change notification settings - Fork 157
Expand file tree
/
Copy pathAcquireTokenSilentSupplier.java
More file actions
171 lines (137 loc) · 8.47 KB
/
AcquireTokenSilentSupplier.java
File metadata and controls
171 lines (137 loc) · 8.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.aad.msal4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URL;
import java.util.Date;
class AcquireTokenSilentSupplier extends AuthenticationResultSupplier {
private static final Logger LOG = LoggerFactory.getLogger(AcquireTokenSilentSupplier.class);
private SilentRequest silentRequest;
protected static final int ACCESS_TOKEN_EXPIRE_BUFFER_IN_SEC = 5 * 60;
AcquireTokenSilentSupplier(AbstractApplicationBase clientApplication, SilentRequest silentRequest) {
super(clientApplication, silentRequest);
this.silentRequest = silentRequest;
}
@Override
AuthenticationResult execute() throws Exception {
boolean shouldRefresh;
Authority requestAuthority = getAuthorityWithPrefNetworkHost(silentRequest.requestAuthority().authority());
AuthenticationResult res;
if (silentRequest.parameters().account() == null) {
res = clientApplication.tokenCache.getCachedAuthenticationResult(
requestAuthority,
silentRequest.parameters().scopes(),
clientApplication.clientId(),
silentRequest.assertion());
} else {
res = clientApplication.tokenCache.getCachedAuthenticationResult(
silentRequest.parameters().account(),
requestAuthority,
silentRequest.parameters().scopes(),
clientApplication.clientId());
if (res == null) {
String message = AuthenticationErrorMessage.NO_TOKEN_IN_CACHE;
clientApplication.log.info(
LogHelper.createMessage(message, silentRequest.requestContext().correlationId()));
throw new MsalClientException(message, AuthenticationErrorCode.CACHE_MISS,
silentRequest.requestContext().correlationId());
}
//Some cached tokens were found, but this metadata will be overwritten if token needs to be refreshed
res.metadata().tokenSource(TokenSource.CACHE);
if (!StringHelper.isBlank(res.accessToken())) {
clientApplication.serviceBundle().getServerSideTelemetry().incrementSilentSuccessfulCount();
}
shouldRefresh = shouldRefresh(silentRequest.parameters(), res);
if (shouldRefresh) {
if (!StringHelper.isBlank(res.refreshToken())) {
//There are certain scenarios where the cached authority may differ from the client app's authority,
// such as when a request is instance aware. Unless overridden by SilentParameters.authorityUrl, the
// cached authority should be used in the token refresh request
if (silentRequest.parameters().authorityUrl() == null && !res.account().environment().equals(requestAuthority.host)) {
requestAuthority = Authority.createAuthority(new URL(requestAuthority.authority().replace(requestAuthority.host(),
res.account().environment())));
}
res = makeRefreshRequest(res, requestAuthority, clientApplication.serviceBundle().getServerSideTelemetry().getCurrentRequest().cacheInfo());
} else {
res = null;
}
}
}
if (res == null || StringHelper.isBlank(res.accessToken())) {
String message = AuthenticationErrorMessage.NO_TOKEN_IN_CACHE;
clientApplication.log.info(
LogHelper.createMessage(message, silentRequest.requestContext().correlationId()));
throw new MsalClientException(message, AuthenticationErrorCode.CACHE_MISS,
silentRequest.requestContext().correlationId());
}
LOG.debug("Returning token from cache");
return res;
}
private AuthenticationResult makeRefreshRequest(AuthenticationResult cachedResult, Authority requestAuthority, CacheRefreshReason refreshReason) throws Exception {
RefreshTokenRequest refreshTokenRequest = new RefreshTokenRequest(
RefreshTokenParameters.builder(silentRequest.parameters().scopes(), cachedResult.refreshToken()).build(),
silentRequest.application(),
silentRequest.requestContext(),
silentRequest);
//The ServiceBundle will have a new CurrentRequest object when the RefreshTokenRequest is made, so the telemetry value needs to be set again
setCacheTelemetry(refreshReason);
AcquireTokenByAuthorizationGrantSupplier acquireTokenByAuthorisationGrantSupplier =
new AcquireTokenByAuthorizationGrantSupplier(clientApplication, refreshTokenRequest, requestAuthority);
try {
AuthenticationResult refreshedResult = acquireTokenByAuthorisationGrantSupplier.execute();
refreshedResult.metadata().tokenSource(TokenSource.IDENTITY_PROVIDER);
refreshedResult.metadata().cacheRefreshReason(refreshReason);
LOG.info("Access token refreshed successfully.");
return refreshedResult;
} catch (MsalServiceException ex) {
//If the token refresh attempt threw a MsalServiceException but the refresh attempt was done
// only because of refreshOn, then simply return the existing cached token rather than throw an exception
if (refreshReason == CacheRefreshReason.PROACTIVE_REFRESH) {
return cachedResult;
}
throw ex;
}
}
//Handles any logic to determine if a token should be refreshed, based on the request parameters and the status of cached tokens
private boolean shouldRefresh(SilentParameters parameters, AuthenticationResult cachedResult) {
//If forceRefresh is true, no reason to check any other option
if (parameters.forceRefresh()) {
setCacheTelemetry(CacheRefreshReason.FORCE_REFRESH);
LOG.debug("Refreshing access token. Cache refresh reason: {}", CacheRefreshReason.FORCE_REFRESH);
return true;
}
//If the request contains claims then the token should be refreshed, to ensure that the returned token has the correct claims
// Note: these are the types of claims found in (for example) a claims challenge, and do not include client capabilities
if (parameters.claims() != null) {
setCacheTelemetry(CacheRefreshReason.CLAIMS);
LOG.debug("Refreshing access token. Cache refresh reason: {}", CacheRefreshReason.CLAIMS);
return true;
}
long currTimeStampSec = new Date().getTime() / 1000;
//If the access token is expired or within 5 minutes of becoming expired, refresh it
if (!StringHelper.isBlank(cachedResult.accessToken()) && cachedResult.expiresOn() < (currTimeStampSec + ACCESS_TOKEN_EXPIRE_BUFFER_IN_SEC)) {
setCacheTelemetry(CacheRefreshReason.EXPIRED);
LOG.debug("Refreshing access token. Cache refresh reason: {}", CacheRefreshReason.EXPIRED);
return true;
}
//Certain long-lived tokens will have a 'refresh on' time that indicates a refresh should be attempted long before the token would expire
if (!StringHelper.isBlank(cachedResult.accessToken()) &&
cachedResult.refreshOn() != null && cachedResult.refreshOn() > 0 &&
cachedResult.refreshOn() < currTimeStampSec && cachedResult.expiresOn() >= (currTimeStampSec + ACCESS_TOKEN_EXPIRE_BUFFER_IN_SEC)){
setCacheTelemetry(CacheRefreshReason.PROACTIVE_REFRESH);
LOG.debug("Refreshing access token. Cache refresh reason: {}", CacheRefreshReason.PROACTIVE_REFRESH);
return true;
}
//If there is a refresh token but no access token, we should use the refresh token to get the access token
if (StringHelper.isBlank(cachedResult.accessToken()) && !StringHelper.isBlank(cachedResult.refreshToken())) {
setCacheTelemetry(CacheRefreshReason.NO_CACHED_ACCESS_TOKEN);
LOG.debug("Refreshing access token. Cache refresh reason: {}", CacheRefreshReason.NO_CACHED_ACCESS_TOKEN);
return true;
}
return false;
}
private void setCacheTelemetry(CacheRefreshReason cacheInfoValue){
clientApplication.serviceBundle().getServerSideTelemetry().getCurrentRequest().cacheInfo(cacheInfoValue);
}
}