Search in sources :

Example 1 with JWSInputException

use of org.keycloak.jose.jws.JWSInputException in project keycloak by keycloak.

the class AbstractShowTokensServlet method renderTokens.

protected String renderTokens(HttpServletRequest req) throws ServletException, IOException {
    RefreshableKeycloakSecurityContext ctx = (RefreshableKeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName());
    String accessTokenPretty = JsonSerialization.writeValueAsPrettyString(ctx.getToken());
    RefreshToken refreshToken;
    try {
        refreshToken = new JWSInput(ctx.getRefreshToken()).readJsonContent(RefreshToken.class);
    } catch (JWSInputException e) {
        throw new IOException(e);
    }
    String refreshTokenPretty = JsonSerialization.writeValueAsPrettyString(refreshToken);
    return new StringBuilder("<span id=\"accessToken\">" + accessTokenPretty + "</span>").append("<span id=\"refreshToken\">" + refreshTokenPretty + "</span>").append("<span id=\"accessTokenString\">" + ctx.getTokenString() + "</span>").append("<span id=\"refreshTokenString\">" + ctx.getRefreshToken() + "</span>").toString();
}
Also used : RefreshToken(org.keycloak.representations.RefreshToken) RefreshableKeycloakSecurityContext(org.keycloak.adapters.RefreshableKeycloakSecurityContext) JWSInputException(org.keycloak.jose.jws.JWSInputException) JWSInput(org.keycloak.jose.jws.JWSInput) IOException(java.io.IOException)

Example 2 with JWSInputException

use of org.keycloak.jose.jws.JWSInputException in project keycloak by keycloak.

the class TestingOIDCEndpointsApplicationResource method requestAuthenticationChannel.

@POST
@Path("/request-authentication-channel")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public Response requestAuthenticationChannel(@Context HttpHeaders headers, AuthenticationChannelRequest request) {
    String rawBearerToken = AppAuthManager.extractAuthorizationHeaderToken(headers);
    AccessToken bearerToken;
    try {
        bearerToken = new JWSInput(rawBearerToken).readJsonContent(AccessToken.class);
    } catch (JWSInputException e) {
        throw new RuntimeException("Failed to parse bearer token", e);
    }
    // required
    String authenticationChannelId = bearerToken.getId();
    if (authenticationChannelId == null)
        throw new BadRequestException("missing parameter : " + HttpAuthenticationChannelProvider.AUTHENTICATION_CHANNEL_ID);
    String loginHint = request.getLoginHint();
    if (loginHint == null)
        throw new BadRequestException("missing parameter : " + CibaGrantType.LOGIN_HINT);
    if (request.getConsentRequired() == null)
        throw new BadRequestException("missing parameter : " + CibaGrantType.IS_CONSENT_REQUIRED);
    String scope = request.getScope();
    if (scope == null)
        throw new BadRequestException("missing parameter : " + OAuth2Constants.SCOPE);
    // optional
    // for testing purpose
    String bindingMessage = request.getBindingMessage();
    if (bindingMessage != null && bindingMessage.equals("GODOWN"))
        throw new BadRequestException("intentional error : GODOWN");
    // only one CIBA flow without binding_message can be accepted per test method by this test mechanism.
    if (bindingMessage == null)
        bindingMessage = ChannelRequestDummyKey;
    authenticationChannelRequests.put(bindingMessage, new TestAuthenticationChannelRequest(request, rawBearerToken));
    return Response.status(Status.CREATED).build();
}
Also used : AccessToken(org.keycloak.representations.AccessToken) JWSInputException(org.keycloak.jose.jws.JWSInputException) BadRequestException(javax.ws.rs.BadRequestException) JWSInput(org.keycloak.jose.jws.JWSInput) TestAuthenticationChannelRequest(org.keycloak.testsuite.rest.representation.TestAuthenticationChannelRequest) Path(javax.ws.rs.Path) POST(javax.ws.rs.POST) Consumes(javax.ws.rs.Consumes) Produces(javax.ws.rs.Produces) NoCache(org.jboss.resteasy.annotations.cache.NoCache)

Example 3 with JWSInputException

use of org.keycloak.jose.jws.JWSInputException in project keycloak by keycloak.

the class TokenEndpoint method permissionGrant.

public Response permissionGrant() {
    event.detail(Details.AUTH_METHOD, "oauth_credentials");
    String accessTokenString = null;
    String authorizationHeader = headers.getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
    if (authorizationHeader != null && authorizationHeader.toLowerCase().startsWith("bearer")) {
        accessTokenString = new AppAuthManager().extractAuthorizationHeaderToken(headers);
    }
    // public clients don't have secret and should be able to obtain a RPT by providing an access token previously issued by the server
    if (accessTokenString != null) {
        AccessToken accessToken = Tokens.getAccessToken(session);
        if (accessToken == null) {
            try {
                // In case the access token is invalid because it's expired or the user is disabled, identify the client
                // from the access token anyway in order to set correct CORS headers.
                AccessToken invalidToken = new JWSInput(accessTokenString).readJsonContent(AccessToken.class);
                ClientModel client = realm.getClientByClientId(invalidToken.getIssuedFor());
                cors.allowedOrigins(session, client);
                event.client(client);
            } catch (JWSInputException ignore) {
            }
            event.error(Errors.INVALID_TOKEN);
            throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Invalid bearer token", Status.UNAUTHORIZED);
        }
        ClientModel client = realm.getClientByClientId(accessToken.getIssuedFor());
        session.getContext().setClient(client);
        cors.allowedOrigins(session, client);
        event.client(client);
    }
    String claimToken = null;
    // claim_token is optional, if provided we just grab it from the request
    if (formParams.containsKey("claim_token")) {
        claimToken = formParams.get("claim_token").get(0);
    }
    String claimTokenFormat = formParams.getFirst("claim_token_format");
    if (claimToken != null && claimTokenFormat == null) {
        claimTokenFormat = AuthorizationTokenService.CLAIM_TOKEN_FORMAT_ID_TOKEN;
    }
    String subjectToken = formParams.getFirst("subject_token");
    if (accessTokenString == null) {
        // in case no bearer token is provided, we force client authentication
        checkClient();
        // if a claim token is provided, we check if the format is a OpenID Connect IDToken and assume the token represents the identity asking for permissions
        if (AuthorizationTokenService.CLAIM_TOKEN_FORMAT_ID_TOKEN.equalsIgnoreCase(claimTokenFormat)) {
            accessTokenString = claimToken;
        } else if (subjectToken != null) {
            accessTokenString = subjectToken;
        } else {
            // Clients need to authenticate in order to obtain a RPT from the server.
            // In order to support cases where the client is obtaining permissions on its on behalf, we issue a temporary access token
            accessTokenString = AccessTokenResponse.class.cast(clientCredentialsGrant().getEntity()).getToken();
        }
    }
    AuthorizationTokenService.KeycloakAuthorizationRequest authorizationRequest = new AuthorizationTokenService.KeycloakAuthorizationRequest(session.getProvider(AuthorizationProvider.class), tokenManager, event, this.request, cors, clientConnection);
    authorizationRequest.setTicket(formParams.getFirst("ticket"));
    authorizationRequest.setClaimToken(claimToken);
    authorizationRequest.setClaimTokenFormat(claimTokenFormat);
    authorizationRequest.setPct(formParams.getFirst("pct"));
    String rpt = formParams.getFirst("rpt");
    if (rpt != null) {
        AccessToken accessToken = session.tokens().decode(rpt, AccessToken.class);
        if (accessToken == null) {
            event.error(Errors.INVALID_REQUEST);
            throw new CorsErrorResponseException(cors, "invalid_rpt", "RPT signature is invalid", Status.FORBIDDEN);
        }
        authorizationRequest.setRpt(accessToken);
    }
    authorizationRequest.setScope(formParams.getFirst("scope"));
    String audienceParam = formParams.getFirst("audience");
    authorizationRequest.setAudience(audienceParam);
    authorizationRequest.setSubjectToken(accessTokenString);
    event.detail(Details.AUDIENCE, audienceParam);
    String submitRequest = formParams.getFirst("submit_request");
    authorizationRequest.setSubmitRequest(submitRequest == null ? true : Boolean.valueOf(submitRequest));
    // permissions have a format like RESOURCE#SCOPE1,SCOPE2
    List<String> permissions = formParams.get("permission");
    if (permissions != null) {
        event.detail(Details.PERMISSION, String.join("|", permissions));
        for (String permission : permissions) {
            String[] parts = permission.split("#");
            String resource = parts[0];
            if (parts.length == 1) {
                authorizationRequest.addPermission(resource);
            } else {
                String[] scopes = parts[1].split(",");
                authorizationRequest.addPermission(parts[0], scopes);
            }
        }
    }
    Metadata metadata = new Metadata();
    String responseIncludeResourceName = formParams.getFirst("response_include_resource_name");
    if (responseIncludeResourceName != null) {
        metadata.setIncludeResourceName(Boolean.parseBoolean(responseIncludeResourceName));
    }
    String responsePermissionsLimit = formParams.getFirst("response_permissions_limit");
    if (responsePermissionsLimit != null) {
        metadata.setLimit(Integer.parseInt(responsePermissionsLimit));
    }
    metadata.setResponseMode(formParams.getFirst("response_mode"));
    authorizationRequest.setMetadata(metadata);
    Response authorizationResponse = AuthorizationTokenService.instance().authorize(authorizationRequest);
    event.success();
    return authorizationResponse;
}
Also used : AuthorizationProvider(org.keycloak.authorization.AuthorizationProvider) Metadata(org.keycloak.representations.idm.authorization.AuthorizationRequest.Metadata) JWSInputException(org.keycloak.jose.jws.JWSInputException) JWSInput(org.keycloak.jose.jws.JWSInput) AccessTokenResponse(org.keycloak.representations.AccessTokenResponse) Response(javax.ws.rs.core.Response) HttpResponse(org.jboss.resteasy.spi.HttpResponse) ClientModel(org.keycloak.models.ClientModel) AuthorizationTokenService(org.keycloak.authorization.authorization.AuthorizationTokenService) AccessToken(org.keycloak.representations.AccessToken) CorsErrorResponseException(org.keycloak.services.CorsErrorResponseException) AccessTokenResponse(org.keycloak.representations.AccessTokenResponse) AppAuthManager(org.keycloak.services.managers.AppAuthManager)

Example 4 with JWSInputException

use of org.keycloak.jose.jws.JWSInputException in project keycloak by keycloak.

the class DefaultTokenExchangeProvider method tokenExchange.

protected Response tokenExchange() {
    UserModel tokenUser = null;
    UserSessionModel tokenSession = null;
    AccessToken token = null;
    String subjectToken = formParams.getFirst(OAuth2Constants.SUBJECT_TOKEN);
    if (subjectToken != null) {
        String subjectTokenType = formParams.getFirst(OAuth2Constants.SUBJECT_TOKEN_TYPE);
        String realmIssuerUrl = Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName());
        String subjectIssuer = formParams.getFirst(OAuth2Constants.SUBJECT_ISSUER);
        if (subjectIssuer == null && OAuth2Constants.JWT_TOKEN_TYPE.equals(subjectTokenType)) {
            try {
                JWSInput jws = new JWSInput(subjectToken);
                JsonWebToken jwt = jws.readJsonContent(JsonWebToken.class);
                subjectIssuer = jwt.getIssuer();
            } catch (JWSInputException e) {
                event.detail(Details.REASON, "unable to parse jwt subject_token");
                event.error(Errors.INVALID_TOKEN);
                throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_TOKEN, "Invalid token type, must be access token", Response.Status.BAD_REQUEST);
            }
        }
        if (subjectIssuer != null && !realmIssuerUrl.equals(subjectIssuer)) {
            event.detail(OAuth2Constants.SUBJECT_ISSUER, subjectIssuer);
            return exchangeExternalToken(subjectIssuer, subjectToken);
        }
        if (subjectTokenType != null && !subjectTokenType.equals(OAuth2Constants.ACCESS_TOKEN_TYPE)) {
            event.detail(Details.REASON, "subject_token supports access tokens only");
            event.error(Errors.INVALID_TOKEN);
            throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_TOKEN, "Invalid token type, must be access token", Response.Status.BAD_REQUEST);
        }
        AuthenticationManager.AuthResult authResult = AuthenticationManager.verifyIdentityToken(session, realm, session.getContext().getUri(), clientConnection, true, true, null, false, subjectToken, headers);
        if (authResult == null) {
            event.detail(Details.REASON, "subject_token validation failure");
            event.error(Errors.INVALID_TOKEN);
            throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_TOKEN, "Invalid token", Response.Status.BAD_REQUEST);
        }
        tokenUser = authResult.getUser();
        tokenSession = authResult.getSession();
        token = authResult.getToken();
    }
    String requestedSubject = formParams.getFirst(OAuth2Constants.REQUESTED_SUBJECT);
    if (requestedSubject != null) {
        event.detail(Details.REQUESTED_SUBJECT, requestedSubject);
        UserModel requestedUser = session.users().getUserByUsername(realm, requestedSubject);
        if (requestedUser == null) {
            requestedUser = session.users().getUserById(realm, requestedSubject);
        }
        if (requestedUser == null) {
            // We always returned access denied to avoid username fishing
            event.detail(Details.REASON, "requested_subject not found");
            event.error(Errors.NOT_ALLOWED);
            throw new CorsErrorResponseException(cors, OAuthErrorException.ACCESS_DENIED, "Client not allowed to exchange", Response.Status.FORBIDDEN);
        }
        if (token != null) {
            event.detail(Details.IMPERSONATOR, tokenUser.getUsername());
            // for this case, the user represented by the token, must have permission to impersonate.
            AdminAuth auth = new AdminAuth(realm, token, tokenUser, client);
            if (!AdminPermissions.evaluator(session, realm, auth).users().canImpersonate(requestedUser)) {
                event.detail(Details.REASON, "subject not allowed to impersonate");
                event.error(Errors.NOT_ALLOWED);
                throw new CorsErrorResponseException(cors, OAuthErrorException.ACCESS_DENIED, "Client not allowed to exchange", Response.Status.FORBIDDEN);
            }
        } else {
            // to impersonate
            if (client.isPublicClient()) {
                event.detail(Details.REASON, "public clients not allowed");
                event.error(Errors.NOT_ALLOWED);
                throw new CorsErrorResponseException(cors, OAuthErrorException.ACCESS_DENIED, "Client not allowed to exchange", Response.Status.FORBIDDEN);
            }
            if (!AdminPermissions.management(session, realm).users().canClientImpersonate(client, requestedUser)) {
                event.detail(Details.REASON, "client not allowed to impersonate");
                event.error(Errors.NOT_ALLOWED);
                throw new CorsErrorResponseException(cors, OAuthErrorException.ACCESS_DENIED, "Client not allowed to exchange", Response.Status.FORBIDDEN);
            }
        }
        tokenSession = session.sessions().createUserSession(realm, requestedUser, requestedUser.getUsername(), clientConnection.getRemoteAddr(), "impersonate", false, null, null);
        if (tokenUser != null) {
            tokenSession.setNote(IMPERSONATOR_ID.toString(), tokenUser.getId());
            tokenSession.setNote(IMPERSONATOR_USERNAME.toString(), tokenUser.getUsername());
        }
        tokenUser = requestedUser;
    }
    String requestedIssuer = formParams.getFirst(OAuth2Constants.REQUESTED_ISSUER);
    if (requestedIssuer == null) {
        return exchangeClientToClient(tokenUser, tokenSession);
    } else {
        try {
            return exchangeToIdentityProvider(tokenUser, tokenSession, requestedIssuer);
        } finally {
            if (subjectToken == null) {
                // we are naked! So need to clean up user session
                try {
                    session.sessions().removeUserSession(realm, tokenSession);
                } catch (Exception ignore) {
                }
            }
        }
    }
}
Also used : UserModel(org.keycloak.models.UserModel) AuthenticationManager(org.keycloak.services.managers.AuthenticationManager) AdminAuth(org.keycloak.services.resources.admin.AdminAuth) UserSessionModel(org.keycloak.models.UserSessionModel) AccessToken(org.keycloak.representations.AccessToken) JWSInputException(org.keycloak.jose.jws.JWSInputException) JWSInput(org.keycloak.jose.jws.JWSInput) CorsErrorResponseException(org.keycloak.services.CorsErrorResponseException) JsonWebToken(org.keycloak.representations.JsonWebToken) OAuthErrorException(org.keycloak.OAuthErrorException) CorsErrorResponseException(org.keycloak.services.CorsErrorResponseException) JWSInputException(org.keycloak.jose.jws.JWSInputException)

Example 5 with JWSInputException

use of org.keycloak.jose.jws.JWSInputException in project keycloak by keycloak.

the class OIDCIdentityProvider method extractIdentity.

protected BrokeredIdentityContext extractIdentity(AccessTokenResponse tokenResponse, String accessToken, JsonWebToken idToken) throws IOException {
    String id = idToken.getSubject();
    BrokeredIdentityContext identity = new BrokeredIdentityContext(id);
    String name = (String) idToken.getOtherClaims().get(IDToken.NAME);
    String givenName = (String) idToken.getOtherClaims().get(IDToken.GIVEN_NAME);
    String familyName = (String) idToken.getOtherClaims().get(IDToken.FAMILY_NAME);
    String preferredUsername = (String) idToken.getOtherClaims().get(getusernameClaimNameForIdToken());
    String email = (String) idToken.getOtherClaims().get(IDToken.EMAIL);
    if (!getConfig().isDisableUserInfoService()) {
        String userInfoUrl = getUserInfoUrl();
        if (userInfoUrl != null && !userInfoUrl.isEmpty()) {
            if (accessToken != null) {
                SimpleHttp.Response response = executeRequest(userInfoUrl, SimpleHttp.doGet(userInfoUrl, session).header("Authorization", "Bearer " + accessToken));
                String contentType = response.getFirstHeader(HttpHeaders.CONTENT_TYPE);
                MediaType contentMediaType;
                try {
                    contentMediaType = MediaType.valueOf(contentType);
                } catch (IllegalArgumentException ex) {
                    contentMediaType = null;
                }
                if (contentMediaType == null || contentMediaType.isWildcardSubtype() || contentMediaType.isWildcardType()) {
                    throw new RuntimeException("Unsupported content-type [" + contentType + "] in response from [" + userInfoUrl + "].");
                }
                JsonNode userInfo;
                if (MediaType.APPLICATION_JSON_TYPE.isCompatible(contentMediaType)) {
                    userInfo = response.asJson();
                } else if (APPLICATION_JWT_TYPE.isCompatible(contentMediaType)) {
                    JWSInput jwsInput;
                    try {
                        jwsInput = new JWSInput(response.asString());
                    } catch (JWSInputException cause) {
                        throw new RuntimeException("Failed to parse JWT userinfo response", cause);
                    }
                    if (verify(jwsInput)) {
                        userInfo = JsonSerialization.readValue(jwsInput.getContent(), JsonNode.class);
                    } else {
                        throw new RuntimeException("Failed to verify signature of userinfo response from [" + userInfoUrl + "].");
                    }
                } else {
                    throw new RuntimeException("Unsupported content-type [" + contentType + "] in response from [" + userInfoUrl + "].");
                }
                id = getJsonProperty(userInfo, "sub");
                name = getJsonProperty(userInfo, "name");
                givenName = getJsonProperty(userInfo, IDToken.GIVEN_NAME);
                familyName = getJsonProperty(userInfo, IDToken.FAMILY_NAME);
                preferredUsername = getUsernameFromUserInfo(userInfo);
                email = getJsonProperty(userInfo, "email");
                AbstractJsonUserAttributeMapper.storeUserProfileForMapper(identity, userInfo, getConfig().getAlias());
            }
        }
    }
    identity.getContextData().put(VALIDATED_ID_TOKEN, idToken);
    identity.setId(id);
    if (givenName != null) {
        identity.setFirstName(givenName);
    }
    if (familyName != null) {
        identity.setLastName(familyName);
    }
    if (givenName == null && familyName == null) {
        identity.setName(name);
    }
    identity.setEmail(email);
    identity.setBrokerUserId(getConfig().getAlias() + "." + id);
    if (preferredUsername == null) {
        preferredUsername = email;
    }
    if (preferredUsername == null) {
        preferredUsername = id;
    }
    identity.setUsername(preferredUsername);
    if (tokenResponse != null && tokenResponse.getSessionState() != null) {
        identity.setBrokerSessionId(getConfig().getAlias() + "." + tokenResponse.getSessionState());
    }
    if (tokenResponse != null)
        identity.getContextData().put(FEDERATED_ACCESS_TOKEN_RESPONSE, tokenResponse);
    if (tokenResponse != null)
        processAccessTokenResponse(identity, tokenResponse);
    return identity;
}
Also used : SimpleHttp(org.keycloak.broker.provider.util.SimpleHttp) MediaType(javax.ws.rs.core.MediaType) JWSInputException(org.keycloak.jose.jws.JWSInputException) JsonNode(com.fasterxml.jackson.databind.JsonNode) JWSInput(org.keycloak.jose.jws.JWSInput) BrokeredIdentityContext(org.keycloak.broker.provider.BrokeredIdentityContext)

Aggregations

JWSInput (org.keycloak.jose.jws.JWSInput)16 JWSInputException (org.keycloak.jose.jws.JWSInputException)16 AccessToken (org.keycloak.representations.AccessToken)8 IOException (java.io.IOException)3 RefreshToken (org.keycloak.representations.RefreshToken)3 JsonNode (com.fasterxml.jackson.databind.JsonNode)2 RefreshableKeycloakSecurityContext (org.keycloak.adapters.RefreshableKeycloakSecurityContext)2 VerificationException (org.keycloak.common.VerificationException)2 JsonWebToken (org.keycloak.representations.JsonWebToken)2 CorsErrorResponseException (org.keycloak.services.CorsErrorResponseException)2 AuthenticationManager (org.keycloak.services.managers.AuthenticationManager)2 X509Certificate (javax.security.cert.X509Certificate)1 BadRequestException (javax.ws.rs.BadRequestException)1 Consumes (javax.ws.rs.Consumes)1 NotAuthorizedException (javax.ws.rs.NotAuthorizedException)1 POST (javax.ws.rs.POST)1 Path (javax.ws.rs.Path)1 Produces (javax.ws.rs.Produces)1 MediaType (javax.ws.rs.core.MediaType)1 Response (javax.ws.rs.core.Response)1