in src/main/java/org/apache/sling/auth/oauth_client/impl/OAuthCallbackServlet.java [120:223]
protected void doGet(@NotNull SlingHttpServletRequest request, @NotNull SlingHttpServletResponse response) throws ServletException {
StringBuffer requestURL = request.getRequestURL();
if ( request.getQueryString() != null )
requestURL.append('?').append(request.getQueryString());
AuthorizationResponse authResponse;
Optional<OAuthState> clientState;
Cookie stateCookie;
try {
authResponse = AuthorizationResponse.parse(new URI(requestURL.toString()));
clientState = stateManager.toOAuthState(authResponse.getState());
if (!clientState.isPresent()) {
logger.debug("Failed state check: no state found in authorization response");
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return;
}
stateCookie = request.getCookie(OAuthStateManager.COOKIE_NAME_REQUEST_KEY);
if (stateCookie == null) {
logger.debug("Failed state check: No request cookie named '{}' found", OAuthStateManager.COOKIE_NAME_REQUEST_KEY);
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return;
}
} catch (ParseException | URISyntaxException e) {
logger.debug("Failed to parse authorization response", e);
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return;
}
try {
String stateFromAuthServer = clientState.get().perRequestKey();
String stateFromClient = stateCookie.getValue();
if (!stateFromAuthServer.equals(stateFromClient)) {
throw new IllegalStateException("Failed state check: request keys from client and server are not the same");
}
if (!authResponse.indicatesSuccess()) {
AuthorizationErrorResponse errorResponse = authResponse.toErrorResponse();
throw new OAuthCallbackException("Authentication failed", new RuntimeException(toErrorMessage("Error in authentication response", errorResponse)));
}
Optional<String> redirect = Optional.ofNullable(clientState.get().redirect());
String authCode = authResponse.toSuccessResponse().getAuthorizationCode().getValue();
String desiredConnectionName = clientState.get().connectionName();
if (desiredConnectionName.isEmpty()) {
throw new IllegalArgumentException("No connection found in clientState");
}
ClientConnection connection = connections.get(desiredConnectionName);
if (connection == null) {
throw new IllegalArgumentException(String.format("Requested unknown connection '%s'", desiredConnectionName));
}
ResolvedOAuthConnection conn = ResolvedOAuthConnection.resolve(connection);
ClientID clientId = new ClientID(conn.clientId());
Secret clientSecret = new Secret(conn.clientSecret());
ClientSecretBasic clientCredentials = new ClientSecretBasic(clientId, clientSecret);
AuthorizationCode code = new AuthorizationCode(authCode);
URI tokenEndpoint = new URI(conn.tokenEndpoint());
TokenRequest tokenRequest = new TokenRequest.Builder(
tokenEndpoint,
clientCredentials,
new AuthorizationCodeGrant(code, new URI(getCallbackUri(request)))
).build();
HTTPRequest httpRequest = tokenRequest.toHTTPRequest();
// GitHub requires an explicitly set Accept header, otherwise the response is url encoded
// https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps#2-users-are-redirected-back-to-your-site-by-github
// see also https://bitbucket.org/connect2id/oauth-2.0-sdk-with-openid-connect-extensions/issues/107/support-application-x-www-form-urlencoded
httpRequest.setAccept("application/json");
HTTPResponse httpResponse = httpRequest.send();
TokenResponse tokenResponse = TokenResponse.parse(httpResponse);
if (!tokenResponse.indicatesSuccess()) {
throw new OAuthCallbackException("Token exchange error", new RuntimeException(toErrorMessage("Error in token response", tokenResponse.toErrorResponse())));
}
OAuthTokens tokens = Converter.toSlingOAuthTokens(tokenResponse.toSuccessResponse().getTokens());
tokenStore.persistTokens(connection, request.getResourceResolver(), tokens);
if (redirect.isEmpty()) {
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
} else {
response.sendRedirect(URLDecoder.decode(redirect.get(), StandardCharsets.UTF_8));
}
} catch (IllegalStateException e) {
throw new OAuthCallbackException("State check failed", e);
} catch (IllegalArgumentException e) {
throw new OAuthCallbackException("Internal error", e);
} catch (ParseException e) {
throw new OAuthCallbackException("Invalid invocation", e);
} catch ( OAuthCallbackException e) {
throw e;
} catch (Exception e) {
throw new OAuthCallbackException("Unknown error", e);
}
}