org.apigw.authserver.svc.impl.TokenServicesImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.apigw.authserver.svc.impl.TokenServicesImpl.java

Source

/**
 *   Copyright 2013 Stockholm County Council
 *
 *   This file is part of APIGW
 *
 *   APIGW is free software; you can redistribute it and/or modify
 *   it under the terms of version 2.1 of the GNU Lesser General Public
 *   License as published by the Free Software Foundation.
 *
 *   APIGW is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public
 *   License along with APIGW; if not, write to the
 *   Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 *   Boston, MA 02111-1307  USA
 *
 */

package org.apigw.authserver.svc.impl;

import static org.joda.time.DateTime.now;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;

import org.apigw.authserver.api.SimpleAuthentication;
import org.apigw.authserver.svc.ResidentServices;
import org.apigw.authserver.svc.TokenServices;
import org.apigw.authserver.svc.repository.AuthorizationGrantRepository;
import org.apigw.authserver.svc.repository.CertifiedClientPermissionRepository;
import org.apigw.authserver.types.domain.AuthorizationGrant;
import org.apigw.authserver.types.domain.CertifiedClientPermission;
import org.apigw.authserver.types.exception.LegalGuardianValidationException;
import org.apigw.commons.logging.CitizenLoggingUtil;
import org.joda.time.DateTime;
import org.joda.time.Period;
import org.joda.time.PeriodType;
import org.joda.time.format.ISODateTimeFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.InvalidScopeException;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.DefaultAuthorizationRequest;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator;
import org.springframework.security.oauth2.provider.token.DefaultAuthenticationKeyGenerator;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class TokenServicesImpl implements TokenServices {

    private static final Logger log = LoggerFactory.getLogger(TokenServicesImpl.class);

    private long refreshTokenValiditySeconds = 60 * 60 * 24 * 30; // default 30 days.

    private long accessTokenValiditySeconds = 60 * 60 * 12; // default 12 hours.

    private boolean supportRefreshToken = false;

    private boolean reuseRefreshToken = true;

    private int legalGuardianAgeLimit = 13; //default

    private int adultAge = 18; // default

    private final AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();
    private ClientDetailsService clientDetailsService;
    private AuthorizationGrantRepository authorizationGrantRepository;
    private CertifiedClientPermissionRepository ccPermissionRepository;
    private ResidentServices residentServices;
    private CitizenLoggingUtil citizenLoggingUtil;

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
        try {
            User user = (User) authentication.getPrincipal();
            String logsafeSSN = citizenLoggingUtil.getLogsafeSSN(user.getUsername());
            log.debug("createAccessToken authentication:{}", logsafeSSN);
        } catch (ClassCastException ignored) {
        }

        String authenticationKey = authenticationKeyGenerator.extractKey(authentication);
        log.debug("createAccessToken -> authenticationKey:{}", authenticationKey);
        AuthorizationGrant grant = authorizationGrantRepository.findByAuthenticationKey(authenticationKey);

        ExpiringOAuth2RefreshToken refreshToken = null;
        if (grant != null && isExpired(grant.getAccessTokenExpires())) { // Timestamp
            if (supportRefreshToken) {
                refreshToken = new DefaultExpiringOAuth2RefreshToken(grant.getRefreshToken(),
                        grant.getGrantExpires());
            }
        } else {
            // grant is either null, or unexpired
            refreshToken = buildRefreshToken(authentication);
        }
        grant = buildAuthorizationGrant(grant, refreshToken, authentication);
        grant = authorizationGrantRepository.save(grant);
        OAuth2AccessToken token = buildAccessTokenFromAuthorizationGrant(grant, false);
        log.debug("Returning from createAccessToken");
        return token;
    }

    /**
     * Builds or updates the data of an AuthorizationGrant object based on an access token, refresh token and
     * an authentication. If a grant is given, it will be updated. If the given authorization grant object is
     * null a new AuthorizationGrant object will be constructed and populated.
     * @param grant The grant to be updated or null if a new AuthorizationGrant is to be created.
     * @param refreshToken The refresh token
     * @param authentication The authentication
     * @return The created or updated authorization grant
     */
    protected AuthorizationGrant buildAuthorizationGrant(AuthorizationGrant grant,
            ExpiringOAuth2RefreshToken refreshToken, OAuth2Authentication authentication) {
        log.debug("buildAuthorizationGrant");
        long now = System.currentTimeMillis();

        boolean update = true;
        if (grant == null) {
            grant = new AuthorizationGrant();
            update = false;
        }

        String clientId = authentication.getAuthorizationRequest().getClientId();
        grant.setClientId(clientId);
        grant.setAccessToken(UUID.randomUUID().toString());

        if (update) {
            grant.getGrantedPermissions().clear();
        } else {
            grant.setGrantedPermissions(new ArrayList<CertifiedClientPermission>(
                    authentication.getAuthorizationRequest().getScope().size()));
        }
        long validitySeconds = accessTokenValiditySeconds;

        for (String permission : authentication.getAuthorizationRequest().getScope()) {
            CertifiedClientPermission ccPermission = ccPermissionRepository.findByPermissionName(permission,
                    clientId);

            if (ccPermission == null) {
                log.warn("No permission {} found for client {}", permission, clientId);
            } else {

                grant.getGrantedPermissions().add(ccPermission);

                int permissionValiditySeconds = ccPermission.getPermission().getAccessTokenValiditySeconds();
                if (permissionValiditySeconds > 0 && validitySeconds > permissionValiditySeconds) {
                    validitySeconds = permissionValiditySeconds;
                }
            }
        }

        final String name = authentication.getName();

        String legalGuardianResidentIdentificationNumber = null;
        String citizenResidentIdentificationNumber = name;
        if (name.contains("/")) {
            final String[] split = name.split("/");
            legalGuardianResidentIdentificationNumber = split[0];
            citizenResidentIdentificationNumber = split[1];
        }

        grant.setLegalGuardianResidentIdentificationNumber(legalGuardianResidentIdentificationNumber);
        grant.setResidentIdentificationNumber(citizenResidentIdentificationNumber);

        if (validitySeconds > 0) {
            final Date expirationTime = generateExpirationTime(citizenResidentIdentificationNumber,
                    validitySeconds);
            grant.setAccessTokenExpires(expirationTime);
        }

        grant.setAuthenticationKey(authenticationKeyGenerator.extractKey(authentication));

        if (supportRefreshToken) {
            grant.setGrantExpires(refreshToken.getExpiration());
            grant.setRefreshToken(refreshToken.getValue());
        } else {
            grant.setGrantExpires(grant.getAccessTokenExpires());
        }
        grant.setIssueDate(new Date(now));

        log.debug("returning from buildAuthorizationGrant");
        return grant;
    }

    /**
     * @param residentIdentificationNumber in format yyyyMMddnnnn- for example 199212319876
     * @param validitySeconds - for example 43200  (60 * 60 * 12) - 12 hours in the future
     * @return
     */
    protected Date generateExpirationTime(String residentIdentificationNumber, long validitySeconds) {
        if (residentIdentificationNumber == null || residentIdentificationNumber.length() < 8) {
            throw new IllegalArgumentException(
                    "Invalid residentIdentificationNumber " + residentIdentificationNumber);
        }
        long validityMilliseconds = validitySeconds * 1000L;
        final String birthdayString = residentIdentificationNumber.substring(0, 8);
        final DateTime birthDate = DateTime.parse(birthdayString, ISODateTimeFormat.basicDate());
        Period period = new Period(birthDate, new DateTime(), PeriodType.yearMonthDay());
        DateTime birthDatePlusLegalGuardianAgeLimit = birthDate.plusYears(legalGuardianAgeLimit);
        DateTime residentAdultDate = birthDate.plusYears(adultAge);
        DateTime expiration = new DateTime().withTimeAtStartOfDay(); //defaulting to midnight of today (token will be immediately invalid)
        if (validitySeconds > 0) {
            // Standard expiration
            final DateTime standardExpiration = new DateTime().plus(validityMilliseconds);
            if (birthDatePlusLegalGuardianAgeLimit.isAfter(now().plus(validityMilliseconds))) { // resident will hit the legal guardian age after max token validity
                expiration = standardExpiration;
            } else if (residentAdultDate.isBeforeNow()) { // resident is considered adult
                expiration = standardExpiration;
            } else if (birthDatePlusLegalGuardianAgeLimit.isAfterNow()) { //resident will hit the legal guardian age before max token validity
                expiration = birthDatePlusLegalGuardianAgeLimit;
            }
            // if we get here resident has passed legal guardian age but is not considered adult, using default
        }
        log.debug("calculated token exp time for resident who is ~ {} years, {} months and {} days old to {}",
                period.getYears(), period.getMonths(), period.getDays(), expiration);
        return expiration.toDate();
    }

    protected ExpiringOAuth2RefreshToken buildRefreshToken(OAuth2Authentication authentication) {
        log.debug("buildRefreshToken");
        if (!supportRefreshToken) {
            return null;
        }
        long validitySeconds = getRefreshTokenValiditySeconds(authentication.getAuthorizationRequest());
        ExpiringOAuth2RefreshToken refreshToken = new DefaultExpiringOAuth2RefreshToken(
                UUID.randomUUID().toString(), new Date(System.currentTimeMillis() + (validitySeconds * 1000L)));
        log.debug("returning from buildRefreshToken");
        return refreshToken;
    }

    /**
     * The refresh token validity period in seconds
     * @param authorizationRequest the current authorization request
     * @return the refresh token validity period in seconds
     */
    protected long getRefreshTokenValiditySeconds(AuthorizationRequest authorizationRequest) {
        log.debug("getRefreshTokenValiditySeconds");
        if (clientDetailsService != null) {
            ClientDetails client = clientDetailsService.loadClientByClientId(authorizationRequest.getClientId());
            if (client.getRefreshTokenValiditySeconds() > 0) {
                log.debug("returning from getRefreshTokenValiditySeconds with clients refresh token validity {}",
                        client.getRefreshTokenValiditySeconds());
                return client.getRefreshTokenValiditySeconds();
            }
        }
        log.debug("returning from getRefreshTokenValiditySeconds with default value {}",
                refreshTokenValiditySeconds);
        return refreshTokenValiditySeconds;
    }

    /**
     * Returns a new access token, shallow-copied from the access token contained in the authorization grant.
     * @param grant The authorization grant holding the access token.
     * @param includeAuthorizationGrantId True if the additional information needs to include authorization_grant_id
     * @return An OAuth2AccessToken populated with information from the given authorization grant.
     */
    protected OAuth2AccessToken buildAccessTokenFromAuthorizationGrant(AuthorizationGrant grant,
            boolean includeAuthorizationGrantId) {
        log.debug("buildAccessTokenFromAuthorizationGrant");
        DefaultOAuth2AccessToken accessToken = new DefaultOAuth2AccessToken(grant.getAccessToken());

        // access token and grant have the same expiry date
        accessToken.setExpiration(grant.getAccessTokenExpires());

        if (supportRefreshToken) {
            accessToken.setRefreshToken(
                    new DefaultExpiringOAuth2RefreshToken(grant.getRefreshToken(), grant.getGrantExpires()));
        }
        accessToken.setScope(buildScopeFromAuthorizationGrant(grant));
        accessToken.setTokenType(OAuth2AccessToken.BEARER_TYPE);
        Map<String, Object> additionalInformation = new HashMap<String, Object>();
        additionalInformation.put("issue_date", grant.getIssueDate());
        if (includeAuthorizationGrantId) {
            additionalInformation.put("authorization_grant_id", grant.getId());
        }

        accessToken.setAdditionalInformation(additionalInformation);
        log.debug("Returning from buildAccessTokenFromAuthorizationGrant");
        return accessToken;
    }

    /**
     * Builds an ordered set of scope names given by a specific authorization grant.
     * @param grant The authorization scope that authorized the scopes.
     * @return An ordered set of strings holding the names of the authorized scopes.
     */
    protected Set<String> buildScopeFromAuthorizationGrant(AuthorizationGrant grant) {
        log.debug("buildScopeFromAuthorizationGrant");
        // Since order is important to get correct authentication key (hash) we need a linked hash set.
        Set<String> scope = new TreeSet<String>();
        if (grant.getGrantedPermissions() != null) {
            for (CertifiedClientPermission grantedPermission : grant.getGrantedPermissions()) {
                scope.add(grantedPermission.getPermissionName());
            }
        }
        log.debug("returning from buildScopeFromAuthorizationGrant");
        return scope;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, AuthorizationRequest request)
            throws AuthenticationException {
        log.debug("refreshAccessToken(refreshTokenValue:{}, request:{})", refreshTokenValue, request);

        if (!supportRefreshToken) {
            throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
        }
        AuthorizationGrant authorizationGrant = authorizationGrantRepository.findByRefreshToken(refreshTokenValue);
        if (authorizationGrant == null) {
            throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
        }

        if (!validateLegalGuardianInAuthrizationGrant(authorizationGrant)) {
            throw new InvalidGrantException(
                    "Authorization grant is missing a valid legal guardian: " + refreshTokenValue);
        }

        OAuth2AccessToken accessToken = buildAccessTokenFromAuthorizationGrant(authorizationGrant, false);
        ExpiringOAuth2RefreshToken refreshToken = (ExpiringOAuth2RefreshToken) accessToken.getRefreshToken();

        if (accessToken == null || accessToken.getRefreshToken() == null) {
            throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
        }

        String clientId = authorizationGrant.getClientId();
        if (clientId == null || !clientId.equals(request.getClientId())) {
            throw new InvalidGrantException("Wrong client for this refresh token: " + refreshTokenValue);
        }

        if (isExpired(accessToken.getRefreshToken())) {
            log.info("Removing expired authorization grant with auth key {} for client {}",
                    authorizationGrant.getAuthenticationKey(), authorizationGrant.getClientId());
            authorizationGrantRepository.delete(authorizationGrant);
            throw new InvalidGrantException("Invalid refresh token: " + accessToken.getRefreshToken());
        }

        Set<String> scope = request.getScope();
        // if scope exists, we want to narrow the scope and therefore check that scope is a subset of the original scope
        // else if the scope param is empty, use the old scope from db.
        if (scope != null && scope.size() > 0) {
            if (accessToken.getScope() == null || !accessToken.getScope().containsAll(scope)) {
                throw new InvalidScopeException(
                        "Unable to narrow the scope of the client authentication to " + scope + ".",
                        accessToken.getScope());
            } else if (accessToken.getScope().size() > scope.size()) {

                // if scope is narrowed, check for already existing accesstoken
                OAuth2Authentication auth = buildAuthenticationFromAuthorizationGrant(authorizationGrant, scope);
                AuthorizationGrant grant = authorizationGrantRepository
                        .findByAuthenticationKey(authenticationKeyGenerator.extractKey(auth));

                log.info("grant: {}", grant);

                if (grant != null) {
                    throw new InvalidScopeException(
                            "Unable to narrow the scope of the client authentication to " + scope
                                    + ". An authorization with that scope, client and user already exists.",
                            accessToken.getScope());
                }
            }
        } else {
            scope = accessToken.getScope();
        }

        OAuth2Authentication authentication = buildAuthenticationFromAuthorizationGrant(authorizationGrant, scope);

        if (!reuseRefreshToken) {
            refreshToken = buildRefreshToken(authentication);
            authorizationGrant.setRefreshToken(refreshToken.getValue());
            authorizationGrant.setGrantExpires(refreshToken.getExpiration());
        }
        authorizationGrant = buildAuthorizationGrant(authorizationGrant, refreshToken, authentication);
        authorizationGrant = authorizationGrantRepository.save(authorizationGrant);
        OAuth2AccessToken token = buildAccessTokenFromAuthorizationGrant(authorizationGrant, false);
        log.debug("Returning from refreshAccessToken");
        return token;
    }

    private OAuth2Authentication buildAuthenticationFromAuthorizationGrant(AuthorizationGrant authorizationGrant,
            Set<String> scope) {
        DefaultAuthorizationRequest authorizationRequest = new DefaultAuthorizationRequest(
                authorizationGrant.getClientId(), scope);
        log.debug("buildAuthenticationFromAuthorizationGrant");
        authorizationRequest = updateAuthorizationRequestWithBasicApiGwData(authorizationRequest);
        Authentication userAuthentication = createAuthentication(
                authorizationGrant.getResidentIdentificationNumber());
        OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(authorizationRequest,
                userAuthentication);
        log.debug("returning from buildAuthenticationFromAuthorizationGrant");
        return oAuth2Authentication;
    }

    /**
      * Create a refreshed authentication.
      *
      * @param authentication The authentication.
      * @param scope The scope for the refreshed token.
      * @return The refreshed authentication.
      * @throws InvalidScopeException If the scope requested is invalid or wider than the original scope.
      */
    protected OAuth2Authentication createRefreshedAuthentication(OAuth2Authentication authentication,
            Set<String> scope) {
        log.debug("createRefreshedAuthentication");
        OAuth2Authentication narrowed = authentication;
        if (scope != null && !scope.isEmpty()) {
            AuthorizationRequest clientAuth = authentication.getAuthorizationRequest();
            Set<String> originalScope = clientAuth.getScope();
            if (originalScope == null || !originalScope.containsAll(scope)) {
                throw new InvalidScopeException(
                        "Unable to narrow the scope of the client authentication to " + scope + ".", originalScope);
            } else {
                narrowed = new OAuth2Authentication(clientAuth, authentication.getUserAuthentication());
            }
        }
        log.debug("returning from createRefreshedAuthentication");
        return narrowed;
    }

    protected boolean isExpired(OAuth2RefreshToken refreshToken) {
        if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
            ExpiringOAuth2RefreshToken expiringToken = (ExpiringOAuth2RefreshToken) refreshToken;
            return expiringToken.getExpiration() == null
                    || System.currentTimeMillis() > expiringToken.getExpiration().getTime();
        }
        return false;
    }

    protected boolean isExpired(Date date) {
        return date == null || System.currentTimeMillis() > date.getTime();
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
        log.debug("getAccessToken(authentication:{})", authentication);
        AuthorizationGrant grant = authorizationGrantRepository
                .findByAuthenticationKey(authenticationKeyGenerator.extractKey(authentication));

        if (grant == null) {
            throw new InvalidGrantException("No access token found for authentication");
        }

        OAuth2AccessToken token = buildAccessTokenFromAuthorizationGrant(grant, true);
        log.debug("returning from getAccessToken");
        return token;
    }

    @Override
    @Transactional(readOnly = true)
    public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException {

        log.debug("loadAuthentication accessToken:{}", accessToken);
        AuthorizationGrant authorizationGrant = authorizationGrantRepository.findByAccessToken(accessToken);
        if (authorizationGrant == null) {
            throw new InvalidTokenException("Invalid access token: " + accessToken);
        } else if (hasExpiredAccessToken(authorizationGrant)) {
            throw new InvalidTokenException("Access token expired: " + accessToken);
        } else if (!validateLegalGuardianInAuthrizationGrant(authorizationGrant)) {
            throw new InvalidGrantException(
                    "Authorization grant is missing a valid legal guardian: " + accessToken);
        }

        OAuth2Authentication auth = buildAuthenticationFromAuthorizationGrant(authorizationGrant,
                buildScopeFromAuthorizationGrant(authorizationGrant));
        log.debug("returning from loadAuthentication");
        return auth;
    }

    private boolean validateLegalGuardianInAuthrizationGrant(AuthorizationGrant authorizationGrant) {
        return authorizationGrant.hasNoLegalGuardian()
                || isLegalGuardianValid(authorizationGrant.getLegalGuardianResidentIdentificationNumber(),
                        authorizationGrant.getResidentIdentificationNumber());
    }

    private boolean isLegalGuardianValid(String legalGuardianResidentId, String citizenResidentId) {
        return residentServices.validateLegalGuardian(legalGuardianResidentId, citizenResidentId);
    }

    private boolean hasExpiredAccessToken(AuthorizationGrant authorizationGrant) {
        return authorizationGrant.getAccessTokenExpires() != null
                && authorizationGrant.getAccessTokenExpires().before(new Date());
    }

    @Override
    @Transactional(readOnly = true)
    public OAuth2AccessToken readAccessToken(String accessToken) {
        log.debug("readAccessToken accessToken:{}", accessToken);
        AuthorizationGrant authorizationGrant = authorizationGrantRepository.findByAccessToken(accessToken);
        OAuth2AccessToken token = buildAccessTokenFromAuthorizationGrant(authorizationGrant, true);
        log.debug("returning from readAccessToken");
        return token;
    }

    protected Collection<OAuth2AccessToken> buildAccessTokenCollectionFromAuthorizationGrants(
            Collection<AuthorizationGrant> authorizationGrants) {
        log.debug("buildAccessTokenCollectionFromAuthorizationGrants");
        Collection<OAuth2AccessToken> tokens = new ArrayList<OAuth2AccessToken>(authorizationGrants.size());
        for (AuthorizationGrant authorizationGrant : authorizationGrants) {
            if (validateLegalGuardianInAuthrizationGrant(authorizationGrant)) {
                tokens.add(buildAccessTokenFromAuthorizationGrant(authorizationGrant, true));
            }
        }
        log.debug("returning from buildAccessTokenCollectionFromAuthorizationGrants");
        return tokens;
    }

    @Override
    @Transactional(readOnly = true)
    public Collection<OAuth2AccessToken> findTokensByUserName(String userName) {
        log.debug("findTokensByUserName userName:{}", userName);
        List<AuthorizationGrant> authorizationGrants = authorizationGrantRepository
                .findByResidentIdentificationNumber(userName);
        Collection<OAuth2AccessToken> tokens = buildAccessTokenCollectionFromAuthorizationGrants(
                authorizationGrants);
        log.debug("returning from findTokensByUserName");
        return tokens;
    }

    @Override
    public Collection<OAuth2AccessToken> findTokensByClientId(String clientId) {
        log.debug("findTokensByClientId clientId:{}", clientId);
        List<AuthorizationGrant> authorizationGrants = authorizationGrantRepository.findByClientId(clientId);
        Collection<OAuth2AccessToken> tokens = buildAccessTokenCollectionFromAuthorizationGrants(
                authorizationGrants);
        log.debug("returning from findTokensByClientId");
        return tokens;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public boolean revokeToken(String tokenValue) {
        log.debug("revokeToken tokenValue:{}", tokenValue);

        //Return immediately
        if (tokenValue == null)
            return false;

        AuthorizationGrant authorizationGrant = authorizationGrantRepository.findByAccessToken(tokenValue);
        if (authorizationGrant == null) {
            log.debug("returning from revokeToken");
            return false;
        } else {
            authorizationGrantRepository.delete(authorizationGrant);
            log.debug("returning from revokeToken, deleted grant");
            return true;
        }
    }

    @Override
    @Transactional(readOnly = true)
    public String getClientId(String tokenValue) {
        log.debug("getClientId tokenValue:{}", tokenValue);
        AuthorizationGrant authorizationGrant = authorizationGrantRepository.findByAccessToken(tokenValue);

        if (authorizationGrant == null) {
            throw new InvalidTokenException("Invalid access token: " + tokenValue);
        }

        String clientId = authorizationGrant.getClientId();
        log.debug("Returning from getClientId");
        return clientId;
    }

    @Override
    @Transactional
    public OAuth2AccessToken createAccessToken(String userId, String clientId, Collection<String> scope)
            throws AuthenticationException {

        //It is important that we use a TreeSet for ordering
        //otherwise the authenticationKey hash will differ the next time
        Set<String> scopeTree = new TreeSet<String>();
        scopeTree.addAll(scope);
        log.debug("createAccessToken(citizenResidentIdentificationNumber: {}, " + "clientId: {}, " + "scope:{})",
                "NOT LOGGED", clientId, scopeTree);

        OAuth2Authentication oauthAuthentication = createOAuth2Authentication(clientId, scopeTree, userId);

        return this.createAccessToken(oauthAuthentication);
    }

    @Override
    @Transactional
    public OAuth2AccessToken createAccessToken(String legalGuardianResidentIdentificationNumber,
            String citizenResidentIdentificationNumber, String clientId, Collection<String> scope)
            throws AuthenticationException, LegalGuardianValidationException {
        //It is important that we use a TreeSet for ordering
        //otherwise the authenticationKey hash will differ the next time
        Set<String> scopeTree = new TreeSet<String>();
        scopeTree.addAll(scope);

        log.debug(
                "createAccessToken(legalGuardianResidentIdentificationNumber: {}, "
                        + "citizenResidentIdentificationNumber: {}, " + "clientId: {}, " + "scope:{})",
                "NOT LOGGED", "NOT LOGGED", clientId, scopeTree);

        if (!isLegalGuardianValid(legalGuardianResidentIdentificationNumber, citizenResidentIdentificationNumber)) {
            throw new LegalGuardianValidationException(legalGuardianResidentIdentificationNumber,
                    citizenResidentIdentificationNumber);
        }

        return this.createAccessToken(
                legalGuardianResidentIdentificationNumber + "/" + citizenResidentIdentificationNumber, clientId,
                scopeTree);
    }

    /*------- Getters/Setters---------*/

    @Override
    public void setRefreshTokenValiditySeconds(long refreshTokenValiditySeconds) {
        this.refreshTokenValiditySeconds = refreshTokenValiditySeconds;
    }

    @Override
    public void setAccessTokenValiditySeconds(long accessTokenValiditySeconds) {
        this.accessTokenValiditySeconds = accessTokenValiditySeconds;
    }

    @Override
    public void setSupportRefreshToken(boolean supportRefreshToken) {
        this.supportRefreshToken = supportRefreshToken;
    }

    public void setLegalGuardianAgeLimit(int legalGuardianAgeLimit) {
        this.legalGuardianAgeLimit = legalGuardianAgeLimit;
    }

    public void setAdultAge(int adultAge) {
        this.adultAge = adultAge;
    }

    public void setClientDetailsService(ClientDetailsService clientDetailsService) {
        this.clientDetailsService = clientDetailsService;
    }

    @Autowired
    public void setCertifiedClientPermissionRepository(CertifiedClientPermissionRepository repository) {
        this.ccPermissionRepository = repository;
    }

    @Override
    public void setReuseRefreshToken(boolean reuseRefreshToken) {
        this.reuseRefreshToken = reuseRefreshToken;
    }

    @Autowired
    public void setAuthorizationGrantRepository(AuthorizationGrantRepository authorizationGrantRepository) {
        this.authorizationGrantRepository = authorizationGrantRepository;
    }

    @Autowired
    public void setResidentServices(ResidentServices residentServices) {
        this.residentServices = residentServices;
    }

    @Autowired
    public void setCitizenLoggingUtil(CitizenLoggingUtil citizenLoggingUtil) {
        this.citizenLoggingUtil = citizenLoggingUtil;
    }

    /**
     * Helper method
     *
     * @param clientId
     * @param scope
     * @param userId
     * @return
     */
    protected OAuth2Authentication createOAuth2Authentication(String clientId, Collection<String> scope,
            String userId) {
        AuthorizationRequest authorizationRequest = createAuthorizationRequest(clientId, scope);
        Authentication citizenAuthentication = createAuthentication(userId);
        OAuth2Authentication oauthAuthentication = new OAuth2Authentication(authorizationRequest,
                citizenAuthentication);
        return oauthAuthentication;
    }

    /**
     * Helper method
     * @param userId
     * @return
     */
    protected Authentication createAuthentication(String userId) {
        User citizenPrincipal = createUserPrincipal(userId);
        Authentication citizenAuthentication = new SimpleAuthentication(citizenPrincipal);
        return citizenAuthentication;
    }

    /**
     * Helper method
     * @param clientId
     * @param scope
     * @return
     */
    protected AuthorizationRequest createAuthorizationRequest(String clientId, Collection<String> scope) {
        DefaultAuthorizationRequest authorizationRequest = new DefaultAuthorizationRequest(clientId, scope);
        return updateAuthorizationRequestWithBasicApiGwData(authorizationRequest);
    }

    protected User createUserPrincipal(String userId) {
        return new User(userId, "", Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")));
    }

    private DefaultAuthorizationRequest updateAuthorizationRequestWithBasicApiGwData(
            DefaultAuthorizationRequest authorizationRequest) {
        authorizationRequest.setApproved(true);
        authorizationRequest.setResourceIds(new HashSet<String>(Arrays.asList("apigw")));
        authorizationRequest
                .setAuthorities(Arrays.asList((GrantedAuthority) new SimpleGrantedAuthority("ROLE_CLIENT")));
        return authorizationRequest;
    }

}