-
Notifications
You must be signed in to change notification settings - Fork 156
Expand file tree
/
Copy pathInteractiveRequest.java
More file actions
143 lines (114 loc) · 6.12 KB
/
InteractiveRequest.java
File metadata and controls
143 lines (114 loc) · 6.12 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
// 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.InetAddress;
import java.net.URI;
import java.net.URL;
import java.net.UnknownHostException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
class InteractiveRequest extends MsalRequest {
private static final Logger LOG = LoggerFactory.getLogger(InteractiveRequest.class);
private AtomicReference<CompletableFuture<IAuthenticationResult>> futureReference;
private InteractiveRequestParameters interactiveRequestParameters;
private String verifier;
private String state;
private PublicClientApplication publicClientApplication;
private URL authorizationUrl;
InteractiveRequest(InteractiveRequestParameters parameters,
AtomicReference<CompletableFuture<IAuthenticationResult>> futureReference,
PublicClientApplication publicClientApplication,
RequestContext requestContext) {
super(publicClientApplication, null, requestContext);
this.interactiveRequestParameters = parameters;
this.futureReference = futureReference;
this.publicClientApplication = publicClientApplication;
validateRedirectUrl(parameters.redirectUri());
}
URL authorizationUrl() {
if (this.authorizationUrl == null) {
authorizationUrl = createAuthorizationUrl();
}
return authorizationUrl;
}
private void validateRedirectUrl(URI redirectUri) {
String host = redirectUri.getHost();
String scheme = redirectUri.getScheme();
InetAddress address;
//Validate URI scheme. Only http is valid, as determined by the HttpListener created in AcquireTokenByInteractiveFlowSupplier.startHttpListener()
if (scheme == null || !scheme.equals("http")) {
String message = String.format(
"Only http://localhost or http://localhost:port is supported for the redirect URI of an interactive request using a browser, but \"%s\" was found. For more information about redirect URI formats, see https://aka.ms/msal4j-interactive-request", scheme);
LOG.error(LogHelper.createMessage(message, this.requestContext().correlationId()));
throw new MsalClientException(message,
AuthenticationErrorCode.LOOPBACK_REDIRECT_URI,
this.requestContext().correlationId());
}
//Ensure that the given redirect URI has a known address
try {
address = InetAddress.getByName(host);
} catch (UnknownHostException e) {
String message = String.format(
"Unknown host exception for host \"%s\". For more information about redirect URI formats, see https://aka.ms/msal4j-interactive-request", host);
LOG.error(LogHelper.createMessage(message, this.requestContext().correlationId()));
throw new MsalClientException(message,
AuthenticationErrorCode.LOOPBACK_REDIRECT_URI,
this.requestContext().correlationId());
}
//Ensure that the redirect URI is considered a loopback address
if (address == null || !address.isLoopbackAddress()) {
String message = "Only loopback redirect URI is supported for interactive requests. For more information about redirect URI formats, see https://aka.ms/msal4j-interactive-request";
LOG.error(LogHelper.createMessage(message, this.requestContext().correlationId()));
throw new MsalClientException(
message,
AuthenticationErrorCode.LOOPBACK_REDIRECT_URI,
this.requestContext().correlationId());
}
}
private URL createAuthorizationUrl() {
AuthorizationRequestUrlParameters.Builder authorizationRequestUrlBuilder =
AuthorizationRequestUrlParameters
.builder(interactiveRequestParameters.redirectUri().toString(),
interactiveRequestParameters.scopes())
.prompt(interactiveRequestParameters.prompt())
.claimsChallenge(interactiveRequestParameters.claimsChallenge())
.loginHint(interactiveRequestParameters.loginHint())
.domainHint(interactiveRequestParameters.domainHint())
.correlationId(publicClientApplication.correlationId())
.instanceAware(interactiveRequestParameters.instanceAware())
.extraQueryParameters(interactiveRequestParameters.extraQueryParameters());
addPkceAndState(authorizationRequestUrlBuilder);
AuthorizationRequestUrlParameters authorizationRequestUrlParameters =
authorizationRequestUrlBuilder.build();
return publicClientApplication.getAuthorizationRequestUrl(
authorizationRequestUrlParameters);
}
private void addPkceAndState(AuthorizationRequestUrlParameters.Builder builder) {
// Create code verifier and code challenge as described in https://tools.ietf.org/html/rfc7636
SecureRandom secureRandom = new SecureRandom();
byte[] randomBytes = new byte[32];
secureRandom.nextBytes(randomBytes);
verifier = Base64.getUrlEncoder().withoutPadding().encodeToString(randomBytes);
state = UUID.randomUUID().toString() + UUID.randomUUID().toString();
builder.codeChallenge(StringHelper.createBase64EncodedSha256Hash(verifier))
.codeChallengeMethod("S256")
.state(state);
}
AtomicReference<CompletableFuture<IAuthenticationResult>> futureReference() {
return this.futureReference;
}
InteractiveRequestParameters interactiveRequestParameters() {
return this.interactiveRequestParameters;
}
String verifier() {
return this.verifier;
}
String state() {
return this.state;
}
}