protected void doGet()

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);
        }
    }