org.cloudfoundry.identity.uaa.oauth.TokenKeyEndpoint.java Source code

Java tutorial

Introduction

Here is the source code for org.cloudfoundry.identity.uaa.oauth.TokenKeyEndpoint.java

Source

/*******************************************************************************
 *     Cloud Foundry
 *     Copyright (c) [2009-2016] Pivotal Software, Inc. All Rights Reserved.
 *
 *     This product is licensed to you under the Apache License, Version 2.0 (the "License").
 *     You may not use this product except in compliance with the License.
 *
 *     This product includes a number of subcomponents with
 *     separate copyright notices and license terms. Your use of these
 *     subcomponents is subject to the terms and conditions of the
 *     subcomponent's license, as noted in the LICENSE file.
 *******************************************************************************/
package org.cloudfoundry.identity.uaa.oauth;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.cloudfoundry.identity.uaa.oauth.token.VerificationKeyResponse;
import org.cloudfoundry.identity.uaa.oauth.token.VerificationKeysListResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Controller;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import java.security.Principal;
import java.security.interfaces.RSAPublicKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static org.cloudfoundry.identity.uaa.oauth.jwk.JsonWebKey.KeyUse.sig;

/**
 * OAuth2 token services that produces JWT encoded token values.
 *
 */
@Controller
public class TokenKeyEndpoint {

    protected final Log logger = LogFactory.getLog(getClass());

    /**
     * Get the verification key for the token signatures. The principal has to
     * be provided only if the key is secret
     * (shared not public).
     *
     * @param principal the currently authenticated user if there is one
     * @return the key used to verify tokens
     */
    @RequestMapping(value = "/token_key", method = RequestMethod.GET)
    @ResponseBody
    public VerificationKeyResponse getKey(Principal principal) {
        KeyInfo key = KeyInfo.getActiveKey();
        if (!includeSymmetricalKeys(principal) && !key.isAssymetricKey()) {
            throw new AccessDeniedException("You need to authenticate to see a shared key");
        }
        return getVerificationKeyResponse(key);
    }

    public static VerificationKeyResponse getVerificationKeyResponse(KeyInfo key) {
        Map<String, Object> result = new HashMap<>();
        result.put("alg", key.getSigner().algorithm());
        result.put("value", key.getVerifierKey());
        //new values per OpenID and JWK spec
        result.put("use", sig.name());
        result.put("kid", key.getKeyId());
        result.put("kty", key.getType());

        if (key.isAssymetricKey() && "RSA".equals(key.getType())) {

            RSAPublicKey rsaKey = key.getRsaPublicKey();
            if (rsaKey != null) {
                String n = Base64Utils.encodeToUrlSafeString(rsaKey.getModulus().toByteArray());
                String e = Base64Utils.encodeToUrlSafeString(rsaKey.getPublicExponent().toByteArray());
                result.put("n", n);
                result.put("e", e);
            }
        }
        return new VerificationKeyResponse(result);
    }

    /**
     * Get the verification key for the token signatures wrapped into keys array.
     * Wrapping done for compatibility with some clients expecting this even for single key, like mod_auth_openidc.
     * The principal has to be provided only if the key is secret
     * (shared not public).
     *
     * @param principal the currently authenticated user if there is one
     * @return the key used to verify tokens, wrapped in keys array
     */
    @RequestMapping(value = "/token_keys", method = RequestMethod.GET)
    @ResponseBody
    public VerificationKeysListResponse getKeys(Principal principal) {
        boolean includeSymmetric = includeSymmetricalKeys(principal);
        Map<String, KeyInfo> keys = KeyInfo.getKeys();
        List<VerificationKeyResponse> keyResponses = keys.values().stream()
                .filter(k -> includeSymmetric || k.isAssymetricKey())
                .map(TokenKeyEndpoint::getVerificationKeyResponse).collect(Collectors.toList());
        return new VerificationKeysListResponse(keyResponses);
    }

    protected boolean includeSymmetricalKeys(Principal principal) {
        if (principal != null) {
            if (principal instanceof AnonymousAuthenticationToken) {
                return false;
            } else if (principal instanceof Authentication) {
                Authentication auth = (Authentication) principal;
                if (auth.getAuthorities() != null) {
                    for (GrantedAuthority authority : auth.getAuthorities()) {
                        if ("uaa.resource".equals(authority.getAuthority())) {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }
}