Skip to content

Commit 8d49ee8

Browse files
committed
[ISSUE #7737]: Support Nonce Validate, add unit tests
1 parent 0660573 commit 8d49ee8

File tree

10 files changed

+635
-149
lines changed

10 files changed

+635
-149
lines changed

plugin-default-impl/nacos-default-auth-plugin/src/main/java/com/alibaba/nacos/plugin/auth/impl/constant/AuthConstants.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,32 @@ public class AuthConstants {
7373
* LDAP Ignore partial result exception.
7474
*/
7575
public static final String NACOS_CORE_AUTH_IGNORE_PARTIAL_RESULT_EXCEPTION = "nacos.core.auth.ldap.ignore.partial.result.exception";
76-
77-
public static final String LDAP_DEFAULT_ENCODED_PASSWORD = PasswordEncoderUtil.encode(System.getProperty("ldap.default.password", "nacos"));
7876

79-
public static final String LDAP_PREFIX = "LDAP_";
77+
public static final String LDAP_DEFAULT_ENCODED_PASSWORD = PasswordEncoderUtil.encode(
78+
System.getProperty("ldap.default.password", "nacos"));
8079

81-
public static final String OIDC_PREFIX = "OIDC_";
80+
public static final String LDAP_PREFIX = "LDAP_";
8281

8382
/**
8483
* Maximum allowed password length.
8584
*/
8685
public static final int MAX_PASSWORD_LENGTH = 72;
86+
87+
/**
88+
* OIDC Related Constants.
89+
*/
90+
91+
public static final String OIDC_PREFIX = "OIDC_";
92+
93+
public static final String OIDC_STATE = "oidc_state";
94+
95+
public static final String OIDC_NONCE = "oidc_nonce";
96+
97+
public static final String OIDC_PARAM_ORIGIN = "origin";
98+
99+
public static final String OIDC_PARAM_TOKEN = "token";
100+
101+
public static final String OIDC_PARAM_MSG = "msg";
102+
103+
public static final String LOGIN_PAGE = "/#/login";
87104
}

plugin-default-impl/nacos-default-auth-plugin/src/main/java/com/alibaba/nacos/plugin/auth/impl/controller/OIDCController.java

Lines changed: 51 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,17 @@
2222
import com.alibaba.nacos.plugin.auth.exception.AccessException;
2323
import com.alibaba.nacos.plugin.auth.impl.constant.AuthConstants;
2424
import com.alibaba.nacos.plugin.auth.impl.oidc.OIDCClient;
25-
import com.alibaba.nacos.plugin.auth.impl.oidc.OIDCConfigurations;
25+
import com.alibaba.nacos.plugin.auth.impl.oidc.OIDCConfigs;
2626
import com.alibaba.nacos.plugin.auth.impl.oidc.OIDCProvider;
2727
import com.alibaba.nacos.plugin.auth.impl.oidc.OIDCService;
28+
import com.alibaba.nacos.plugin.auth.impl.oidc.OIDCState;
2829
import com.alibaba.nacos.plugin.auth.impl.users.NacosUser;
2930
import com.fasterxml.jackson.databind.node.ObjectNode;
31+
import com.nimbusds.jose.util.Base64URL;
3032
import com.nimbusds.oauth2.sdk.AuthorizationCode;
3133
import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
3234
import com.nimbusds.openid.connect.sdk.claims.UserInfo;
35+
import org.springframework.beans.factory.annotation.Autowired;
3336
import org.springframework.web.bind.annotation.GetMapping;
3437
import org.springframework.web.bind.annotation.RequestMapping;
3538
import org.springframework.web.bind.annotation.RequestParam;
@@ -60,19 +63,40 @@ public class OIDCController {
6063

6164
private final OIDCService oidcService;
6265

66+
@Autowired
6367
public OIDCController(OIDCClient oidcClient, OIDCService oidcService) {
6468
this.oidcClient = oidcClient;
6569
this.oidcService = oidcService;
6670
}
6771

72+
private static String buildRedirectUriWithPayload(String origin, String resultCode, String result) {
73+
// split the origin URL into base URL and hash route
74+
int hashIndex = origin.indexOf('#');
75+
String baseUrl = hashIndex != -1 ? origin.substring(0, hashIndex) : origin;
76+
String hashRoute = hashIndex != -1 ? origin.substring(hashIndex) : "";
77+
78+
// build redirect url with hash route and token
79+
UriComponentsBuilder redirectUriBuilder = UriComponentsBuilder.fromUriString(baseUrl);
80+
if (!hashRoute.isEmpty()) {
81+
StringBuilder sb = new StringBuilder(hashRoute);
82+
if (hashRoute.contains("?")) {
83+
sb.append("&");
84+
} else {
85+
sb.append("?");
86+
}
87+
hashRoute = sb.append(resultCode).append("=").append(result).toString();
88+
}
89+
return redirectUriBuilder.build().toUriString() + hashRoute;
90+
}
91+
6892
/**
6993
* Get current OIDC provider.
7094
*/
7195
@GetMapping("/provider")
7296
public OIDCProvider getProvider() {
73-
String providerKey = OIDCConfigurations.getProvider();
97+
String providerKey = OIDCConfigs.getProvider();
7498
OIDCProvider provider = new OIDCProvider();
75-
provider.setName(OIDCConfigurations.getNameByKey(providerKey));
99+
provider.setName(OIDCConfigs.getNameByKey(providerKey));
76100
provider.setKey(providerKey);
77101
return provider;
78102

@@ -89,13 +113,12 @@ public OIDCProvider getProvider() {
89113
public void startAuthentication(@RequestParam("origin") String origin, HttpServletResponse response,
90114
HttpSession session) throws IOException {
91115

92-
String callbackUri = ServletUriComponentsBuilder.fromCurrentContextPath().path(CALLBACK_PATH)
93-
.queryParam("origin", origin).toUriString();
116+
String callbackUri = ServletUriComponentsBuilder.fromCurrentContextPath().path(CALLBACK_PATH).toUriString();
94117

95-
AuthenticationRequest authRequest = oidcClient.createAuthenticationRequest(callbackUri);
118+
AuthenticationRequest authRequest = oidcClient.createAuthenticationRequest(callbackUri, origin);
96119

97-
session.setAttribute("oidc_state", authRequest.getState().getValue());
98-
session.setAttribute("oidc_nonce", authRequest.getNonce().getValue());
120+
session.setAttribute(AuthConstants.OIDC_STATE, authRequest.getState().getValue());
121+
session.setAttribute(AuthConstants.OIDC_NONCE, authRequest.getNonce().getValue());
99122

100123
// Redirect to Authentication endpoint
101124
response.sendRedirect(authRequest.toURI().toString());
@@ -112,29 +135,39 @@ public void startAuthentication(@RequestParam("origin") String origin, HttpServl
112135
*/
113136
@GetMapping("/callback")
114137
public void callback(@RequestParam("code") String code, @RequestParam("state") String returnedState,
115-
@RequestParam("origin") String origin, HttpServletResponse response, HttpSession session)
116-
throws IOException {
138+
HttpServletResponse response, HttpSession session) throws IOException {
117139

118140
// Check if the state is valid
119-
String originalState = (String) session.getAttribute("oidc_state");
120-
if (originalState == null || !originalState.equals(returnedState)) {
121-
String uriString = buildRedirectUriWithPayload(origin, "msg", "Invalid state");
141+
String originalState = (String) session.getAttribute(AuthConstants.OIDC_STATE);
142+
if (originalState == null) {
143+
String missingOrigin = ServletUriComponentsBuilder.fromCurrentContextPath().path(AuthConstants.LOGIN_PAGE)
144+
.toUriString();
145+
String uriString = buildRedirectUriWithPayload(missingOrigin, AuthConstants.OIDC_PARAM_MSG,
146+
"Session expired");
147+
response.sendRedirect(uriString);
148+
return;
149+
}
150+
String json = Base64URL.from(originalState).decodeToString();
151+
OIDCState state = JacksonUtils.toObj(json, OIDCState.class);
152+
if (!originalState.equals(returnedState)) {
153+
String uriString = buildRedirectUriWithPayload(state.getOrigin(), AuthConstants.OIDC_PARAM_MSG,
154+
"Invalid state");
122155
response.sendRedirect(uriString);
123156
return;
124157
}
125158

126159
// Exchange the authorization code for the information
127-
String callbackUri = ServletUriComponentsBuilder.fromCurrentContextPath().path(CALLBACK_PATH)
128-
.queryParam("origin", origin).toUriString();
129-
UserInfo userInfo = oidcClient.getUserInfo(new AuthorizationCode(code), callbackUri);
160+
String callbackUri = ServletUriComponentsBuilder.fromCurrentContextPath().path(CALLBACK_PATH).toUriString();
161+
UserInfo userInfo = oidcClient.getUserInfo(new AuthorizationCode(code), callbackUri, state.getNonce());
130162

131163
// Extract the username from the user info
132164
String preferredUsername = userInfo.getPreferredUsername();
133165
NacosUser nacosUser;
134166
try {
135167
nacosUser = oidcService.getUser(preferredUsername);
136168
} catch (AccessException e) {
137-
String uriString = buildRedirectUriWithPayload(origin, "msg", "User not found");
169+
String uriString = buildRedirectUriWithPayload(state.getOrigin(), AuthConstants.OIDC_PARAM_MSG,
170+
"User not found");
138171
response.sendRedirect(uriString);
139172
return;
140173
}
@@ -153,30 +186,10 @@ public void callback(@RequestParam("code") String code, @RequestParam("state") S
153186

154187
byte[] resultCodedBytes = Base64.encodeBase64(result.toString().getBytes(StandardCharsets.UTF_8));
155188

156-
String uriString = buildRedirectUriWithPayload(origin, "token",
189+
String uriString = buildRedirectUriWithPayload(state.getOrigin(), AuthConstants.OIDC_PARAM_TOKEN,
157190
new String(resultCodedBytes, StandardCharsets.UTF_8));
158191

159192
response.sendRedirect(uriString);
160193
}
161194

162-
private static String buildRedirectUriWithPayload(String origin, String resultCode, String result) {
163-
// split the origin URL into base URL and hash route
164-
int hashIndex = origin.indexOf('#');
165-
String baseUrl = hashIndex != -1 ? origin.substring(0, hashIndex) : origin;
166-
String hashRoute = hashIndex != -1 ? origin.substring(hashIndex) : "";
167-
168-
// build redirect url with hash route and token
169-
UriComponentsBuilder redirectUriBuilder = UriComponentsBuilder.fromUriString(baseUrl);
170-
if (!hashRoute.isEmpty()) {
171-
StringBuilder sb = new StringBuilder(hashRoute);
172-
if (hashRoute.contains("?")) {
173-
sb.append("&");
174-
} else {
175-
sb.append("?");
176-
}
177-
hashRoute = sb.append(resultCode).append("=").append(result).toString();
178-
}
179-
return redirectUriBuilder.build().toUriString() + hashRoute;
180-
}
181-
182195
}

0 commit comments

Comments
 (0)