All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.adorsys.sts.token.tokenexchange.JwtTokenExchangeService Maven / Gradle / Ivy

The newest version!
package de.adorsys.sts.token.tokenexchange;

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JOSEObjectType;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import de.adorsys.sts.common.converter.KeyConverter;
import de.adorsys.sts.common.model.KeyAndJwk;
import de.adorsys.sts.keymanagement.service.KeyManagementService;
import de.adorsys.sts.token.InvalidParameterException;
import de.adorsys.sts.token.MissingParameterException;
import de.adorsys.sts.token.api.TokenResponse;
import de.adorsys.sts.tokenauth.BearerToken;
import de.adorsys.sts.tokenauth.BearerTokenValidator;
import org.apache.commons.lang3.StringUtils;

import java.time.Clock;
import java.util.*;

public class JwtTokenExchangeService implements TokenExchangeService {
    private final TokenExchangeClaimsService tokenExchangeClaimsService;
    private final KeyManagementService keyManager;
    private final BearerTokenValidator bearerTokenValidator;
    private final Clock clock;

    public JwtTokenExchangeService(
            TokenExchangeClaimsService tokenExchangeClaimsService,
            KeyManagementService keyManager,
            BearerTokenValidator bearerTokenValidator,
            Clock clock
    ) {
        this.tokenExchangeClaimsService = tokenExchangeClaimsService;
        this.keyManager = keyManager;
        this.bearerTokenValidator = bearerTokenValidator;
        this.clock = clock;
    }

    public TokenResponse exchangeToken(TokenExchangeRequest tokenExchange) {
        return exchangeToken(
                tokenExchange.getGrantType(),
                tokenExchange.getResources(),
                tokenExchange.getSubjectToken(),
                tokenExchange.getSubjectTokenType(),
                tokenExchange.getActorToken(),
                tokenExchange.getActorTokenType(),
                tokenExchange.getIssuer(),
                tokenExchange.getScope(),
                tokenExchange.getRequestedTokenType(),
                tokenExchange.getAudiences()
                );
    }

    private TokenResponse exchangeToken(
            String grant_type,
            String[] resources,
            String subject_token,
            String subject_token_type,
            String actor_token,
            String actor_token_type,
            String issuer,
            String scope,
            String requested_token_type,
            String[] audiences
    )
            throws InvalidParameterException, MissingParameterException, TokenValidationException {
        // Validate input parameters.
        if (!StringUtils.equals(TokenExchangeConstants.TOKEN_EXCHANGE_OAUTH_GRANT_TYPE, grant_type)) {
            throw new InvalidParameterException("Request parameter grant_type is missing or does not carry the value urn:ietf:params:oauth:grant-type:token-exchange. See https://tools.ietf.org/html/draft-ietf-oauth-token-exchange-08#section-2.1");
        }

        if (StringUtils.isBlank(subject_token)) {
            throw new MissingParameterException("subject_token");
        }

        if (StringUtils.isBlank(subject_token_type)) {
            throw new MissingParameterException("subject_token_type");
        }

        // If requested token type is not null, then the value must be urn:ietf:params:oauth:token-type:jwt
        if (StringUtils.isNotBlank(requested_token_type) && !StringUtils.equals(TokenExchangeConstants.JWT_OAUTH_TOKEN_TYPE, requested_token_type)) {
            throw new InvalidParameterException("Request parameter requested_token_type must be left blank or carry the value urn:ietf:params:oauth:token-type:jwt. Only JWT token types are supported by this version");
        }

        if (!StringUtils.equals(TokenExchangeConstants.JWT_OAUTH_TOKEN_TYPE, subject_token_type)) {
            throw new InvalidParameterException("Request parameter subject_token_type is missing or does not carry the value urn:ietf:params:oauth:token-type:jwt. Only JWT token types can be consumed by this version");
        }

        BearerToken subjectBearerToken = bearerTokenValidator.extract(subject_token);

        if (!subjectBearerToken.isValid()) {
            String tokenName = "subject_token";
            throw new TokenValidationException("Token in field " + tokenName + " does not seam to be a valid token");
        }

        JWTClaimsSet actorTokenClaim = null;
        if (StringUtils.isNotBlank(actor_token)) {
            // If actor token is not null, then the value of the actor_token_type must be urn:ietf:params:oauth:token-type:jwt
            if (!StringUtils.equals(TokenExchangeConstants.JWT_OAUTH_TOKEN_TYPE, actor_token_type)) {
                throw new InvalidParameterException("The conditional parameter actor_token_type must be set when actor_token is sent and carry the value urn:ietf:params:oauth:token-type:jwt. Only JWT token types are supported by this version");
            }

            BearerToken actorBearerToken = bearerTokenValidator.extract(actor_token);

            if (!actorBearerToken.isValid()) {
                String tokenName = "actor_token";
                throw new TokenValidationException("Token in field " + tokenName + " does not seam to be a valid token");
            }

            actorTokenClaim = actorBearerToken.getClaims();
        }

        JWTClaimsSet subjectTokenClaims = subjectBearerToken.getClaims();

        JWTClaimsSet.Builder claimSetBuilder = new JWTClaimsSet.Builder();
        claimSetBuilder = claimSetBuilder.subject(subjectTokenClaims.getSubject())
                .expirationTime(subjectTokenClaims.getExpirationTime())
                .issuer(issuer)
                .issueTime(new Date(clock.instant().toEpochMilli()))
                .jwtID(UUID.randomUUID().toString())
                .notBeforeTime(subjectTokenClaims.getNotBeforeTime())
                .claim("typ", "Bearer")
                .claim("acr", subjectTokenClaims.getClaim("acr"))
                .claim("role", "USER");
// preferred_username

        // Dealing with scope
        List existingScope = subjectBearerToken.getRoles();
        List newScopeList = existingScope;
        if (StringUtils.isNotBlank(scope)) {
            newScopeList = new ArrayList<>();
            String[] scopes = StringUtils.split(scope);
            for (String scopeStr : scopes) {
                if (existingScope.contains(scopeStr)) {
                    newScopeList.add(scopeStr);
                }
            }
        }
        if (!newScopeList.isEmpty()) {
            claimSetBuilder.claim("scp", newScopeList);
        }

        tokenExchangeClaimsService.extendClaims(
                claimSetBuilder,
                audiences,
                resources,
                subjectTokenClaims.getSubject()
        );

        // Actor Token
        if (actorTokenClaim != null) {
            HashMap hashMap = new HashMap<>();
            hashMap.put("sub", actorTokenClaim.getSubject());
            hashMap.put("iss", actorTokenClaim.getIssuer());
            Object nestedActor = actorTokenClaim.getClaim("act");
            if (nestedActor != null) {
                hashMap.put("act", nestedActor);
            }
            claimSetBuilder = claimSetBuilder.claim("act", hashMap);
        }

        JWTClaimsSet jwtClaimsSet = claimSetBuilder.build();

        KeyAndJwk randomKey = keyManager.randomSignKey();
        JWSAlgorithm jwsAlgo = KeyConverter.getJWSAlgo(randomKey);

        JWSHeader jwsHeader = new JWSHeader.Builder(jwsAlgo)
                .type(JOSEObjectType.JWT)
                .keyID(randomKey.jwk.getKeyID())
                .build();

        SignedJWT signedJWT = new SignedJWT(jwsHeader, jwtClaimsSet);
        try {
            signedJWT.sign(KeyConverter.findSigner(randomKey));
        } catch (JOSEException e) {
            throw new IllegalStateException(e);
        }

        TokenResponse tokenResponse = new TokenResponse();
        tokenResponse.setAccess_token(signedJWT.serialize());
        tokenResponse.setIssued_token_type(TokenResponse.ISSUED_TOKEN_TYPE_ACCESS_TOKEN);
        tokenResponse.setToken_type(TokenResponse.TOKEN_TYPE_BEARER);
        int expiresIn = (int) ((jwtClaimsSet.getExpirationTime().getTime() - new Date(clock.instant().toEpochMilli()).getTime()) / 1000);
        tokenResponse.setExpires_in(expiresIn);

        StringJoiner joiner = new StringJoiner(" ");
        for (String scopeStr : newScopeList) {
            joiner.add(scopeStr);
        }

        tokenResponse.setScope(joiner.toString());

        return tokenResponse;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy