org.mitre.oauth2.token.JWTAssertionTokenGranter.java Source code

Java tutorial

Introduction

Here is the source code for org.mitre.oauth2.token.JWTAssertionTokenGranter.java

Source

/*******************************************************************************
 * Copyright 2016 The MITRE Corporation
 *   and the MIT Internet Trust Consortium
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
/**
 * 
 */
package org.mitre.oauth2.token;

import java.text.ParseException;
import java.util.Date;
import java.util.UUID;

import org.mitre.jwt.signer.service.JWTSigningAndValidationService;
import org.mitre.oauth2.model.ClientDetailsEntity;
import org.mitre.oauth2.model.OAuth2AccessTokenEntity;
import org.mitre.oauth2.service.ClientDetailsEntityService;
import org.mitre.oauth2.service.OAuth2TokenEntityService;
import org.mitre.oauth2.service.SystemScopeService;
import org.mitre.openid.connect.config.ConfigurationPropertiesBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
import org.springframework.stereotype.Component;

import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.JWTParser;
import com.nimbusds.jwt.SignedJWT;

/**
 * @author jricher
 *
 */
@Component("jwtAssertionTokenGranter")
public class JWTAssertionTokenGranter extends AbstractTokenGranter {

    private static final String grantType = "urn:ietf:params:oauth:grant-type:jwt-bearer";

    // keep down-cast versions so we can get to the right queries
    private OAuth2TokenEntityService tokenServices;

    @Autowired
    private JWTSigningAndValidationService jwtService;

    @Autowired
    private ConfigurationPropertiesBean config;

    @Autowired
    public JWTAssertionTokenGranter(OAuth2TokenEntityService tokenServices,
            ClientDetailsEntityService clientDetailsService, OAuth2RequestFactory requestFactory) {
        super(tokenServices, clientDetailsService, requestFactory, grantType);
        this.tokenServices = tokenServices;
    }

    /* (non-Javadoc)
     * @see org.springframework.security.oauth2.provider.token.AbstractTokenGranter#getOAuth2Authentication(org.springframework.security.oauth2.provider.AuthorizationRequest)
     */
    @Override
    protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest)
            throws AuthenticationException, InvalidTokenException {
        // read and load up the existing token
        String incomingTokenValue = tokenRequest.getRequestParameters().get("assertion");
        OAuth2AccessTokenEntity incomingToken = tokenServices.readAccessToken(incomingTokenValue);

        if (incomingToken.getScope().contains(SystemScopeService.ID_TOKEN_SCOPE)) {

            if (!client.getClientId().equals(tokenRequest.getClientId())) {
                throw new InvalidClientException("Not the right client for this token");
            }

            // it's an ID token, process it accordingly

            try {

                // TODO: make this use a more specific idtoken class
                JWT idToken = JWTParser.parse(incomingTokenValue);

                OAuth2AccessTokenEntity accessToken = tokenServices.getAccessTokenForIdToken(incomingToken);

                if (accessToken != null) {

                    //OAuth2AccessTokenEntity newIdToken = tokenServices.get

                    OAuth2AccessTokenEntity newIdTokenEntity = new OAuth2AccessTokenEntity();

                    // copy over all existing claims
                    JWTClaimsSet.Builder claims = new JWTClaimsSet.Builder(idToken.getJWTClaimsSet());

                    if (client instanceof ClientDetailsEntity) {

                        ClientDetailsEntity clientEntity = (ClientDetailsEntity) client;

                        // update expiration and issued-at claims
                        if (clientEntity.getIdTokenValiditySeconds() != null) {
                            Date expiration = new Date(System.currentTimeMillis()
                                    + (clientEntity.getIdTokenValiditySeconds() * 1000L));
                            claims.expirationTime(expiration);
                            newIdTokenEntity.setExpiration(expiration);
                        }

                    } else {
                        //This should never happen
                        logger.fatal("SEVERE: Client is not an instance of OAuth2AccessTokenEntity.");
                        throw new BadCredentialsException(
                                "SEVERE: Client is not an instance of ClientDetailsEntity; JwtAssertionTokenGranter cannot process this request.");
                    }

                    claims.issueTime(new Date());
                    claims.jwtID(UUID.randomUUID().toString()); // set a random NONCE in the middle of it

                    SignedJWT newIdToken = new SignedJWT((JWSHeader) idToken.getHeader(), claims.build());
                    jwtService.signJwt(newIdToken);

                    newIdTokenEntity.setJwt(newIdToken);
                    newIdTokenEntity.setAuthenticationHolder(incomingToken.getAuthenticationHolder());
                    newIdTokenEntity.setScope(incomingToken.getScope());
                    newIdTokenEntity.setClient(incomingToken.getClient());

                    newIdTokenEntity = tokenServices.saveAccessToken(newIdTokenEntity);

                    // attach the ID token to the access token entity
                    accessToken.setIdToken(newIdTokenEntity);
                    accessToken = tokenServices.saveAccessToken(accessToken);

                    // delete the old ID token
                    tokenServices.revokeAccessToken(incomingToken);

                    return newIdTokenEntity;

                }
            } catch (ParseException e) {
                logger.warn("Couldn't parse id token", e);
            }

        }

        // if we got down here, we didn't actually create any tokens, so return null

        return null;

        /*
         * Otherwise, process it like an access token assertion ... which we don't support yet so this is all commented out
         * /
         if (jwtService.validateSignature(incomingTokenValue)) {
            
            Jwt jwt = Jwt.parse(incomingTokenValue);
            
            
            if (oldToken.getScope().contains("id-token")) {
         // TODO: things
            }
            
            // TODO: should any of these throw an exception instead of returning null?
            JwtClaims claims = jwt.getClaims();
            if (!config.getIssuer().equals(claims.getIssuer())) {
         // issuer isn't us
         return null;
            }
            
            if (!authorizationRequest.getClientId().equals(claims.getAudience())) {
         // audience isn't the client
         return null;
            }
            
            Date now = new Date();
            if (!now.after(claims.getExpiration())) {
         // token is expired
         return null;
            }
            
            // FIXME
            // This doesn't work. We need to look up the old token, figure out its scopes and bind it appropriately.
            // In the case of an ID token, we need to look up its parent access token and change the reference, and revoke the old one, and
            // that's tricky.
            // we might need new calls on the token services layer to handle this, and we might
            // need to handle id tokens separately.
            return new OAuth2Authentication(authorizationRequest, null);
            
         } else {
            return null; // throw error??
         }
         */

    }

}