Search in sources :

Example 1 with IdToken

use of com.yahoo.athenz.auth.token.IdToken in project athenz by yahoo.

the class ZTSImpl method getOIDCResponse.

@Override
public Response getOIDCResponse(ResourceContext ctx, String responseType, String clientId, String redirectUri, String scope, String state, String nonce, String keyType) {
    final String caller = ctx.getApiName();
    final String principalDomain = logPrincipalAndGetDomain(ctx);
    validateRequest(ctx.request(), principalDomain, caller);
    validate(nonce, TYPE_ENTITY_NAME, principalDomain, caller);
    validate(clientId, TYPE_SERVICE_NAME, principalDomain, caller);
    if (!StringUtil.isEmpty(state)) {
        validate(state, TYPE_ENTITY_NAME, principalDomain, caller);
    }
    if (!StringUtil.isEmpty(keyType)) {
        validate(keyType, TYPE_SIMPLE_NAME, principalDomain, caller);
    }
    clientId = clientId.toLowerCase();
    // get our principal's name
    final Principal principal = ((RsrcCtxWrapper) ctx).principal();
    String principalName = principal.getFullName();
    // verify we have a valid client id
    final String domainName = AthenzUtils.extractPrincipalDomainName(clientId);
    if (domainName == null) {
        throw requestError("Invalid client id", caller, principal.getDomain(), principalDomain);
    }
    setRequestDomain(ctx, domainName);
    // first retrieve our domain data object from the cache
    DataCache data = dataStore.getDataCache(domainName);
    if (data == null) {
        setRequestDomain(ctx, ZTSConsts.ZTS_UNKNOWN_DOMAIN);
        throw notFoundError("No such domain: " + domainName, caller, ZTSConsts.ZTS_UNKNOWN_DOMAIN, principalDomain);
    }
    final String serviceEndpoint = extractServiceEndpoint(data.getDomainData(), clientId);
    if (StringUtil.isEmpty(serviceEndpoint)) {
        throw requestError("Invalid Service or empty service endpoint", caller, principal.getDomain(), principalDomain);
    }
    if (!serviceEndpoint.equalsIgnoreCase(redirectUri)) {
        throw requestError("Unregistered redirect uri", caller, principal.getDomain(), principalDomain);
    }
    if (!ZTSConsts.ZTS_OPENID_RESPONSE_IT_ONLY.equals(responseType)) {
        return oidcError("invalid response type", redirectUri, state);
    }
    if (StringUtil.isEmpty(scope)) {
        return oidcError("no scope provided", redirectUri, state);
    }
    // our scopes are space separated list of values. Any groups
    // scopes are preferred over any role scopes
    IdTokenRequest tokenRequest = new IdTokenRequest(scope);
    if (tokenRequest.getDomainName() != null && !domainName.equalsIgnoreCase(tokenRequest.getDomainName())) {
        return oidcError("domain name mismatch", redirectUri, state);
    }
    // check if the authorized service domain matches to the
    // requested domain name
    checkRoleTokenAuthorizedServiceRequest(principal, domainName, caller);
    // validate principal object to make sure we're not
    // processing a role identity, and instead we require
    // a service identity
    validatePrincipalNotRoleIdentity(principal, caller);
    // now let's process our requests and see if we need to extract
    // either groups or roles for our response
    List<String> groups = null;
    if (tokenRequest.isGroupsScope()) {
        Set<String> groupNames = tokenRequest.getGroupNames();
        if (groupNames != null) {
            for (String groupName : groupNames) {
                validate(groupName, TYPE_ENTITY_NAME, principalDomain, caller);
            }
        }
        // process our request and retrieve the groups for the principal
        groups = dataStore.getPrincipalGroups(principalName, domainName, groupNames);
        if (groupNames != null && groups == null) {
            return oidcError("principal not included in requested groups", redirectUri, state);
        }
    } else if (tokenRequest.isRolesScope()) {
        String[] requestedRoles = tokenRequest.getRoleNames();
        if (requestedRoles != null) {
            for (String requestedRole : requestedRoles) {
                validate(requestedRole, TYPE_ENTITY_NAME, principalDomain, caller);
            }
        }
        // process our request and retrieve the roles for the principal
        Set<String> roles = new HashSet<>();
        dataStore.getAccessibleRoles(data, domainName, principalName, requestedRoles, roles, false);
        if (requestedRoles != null && roles.isEmpty()) {
            return oidcError("principal not included in requested roles", redirectUri, state);
        }
        groups = new ArrayList<>(roles);
    }
    long iat = System.currentTimeMillis() / 1000;
    IdToken idToken = new IdToken();
    idToken.setVersion(1);
    idToken.setAudience(clientId);
    idToken.setSubject(principalName);
    idToken.setIssuer(ztsOpenIDIssuer);
    idToken.setNonce(nonce);
    idToken.setGroups(groups);
    idToken.setIssueTime(iat);
    idToken.setAuthTime(iat);
    idToken.setExpiryTime(iat + idTokenDefaultTimeout);
    ServerPrivateKey signPrivateKey = getSignPrivateKey(keyType);
    String location = redirectUri + "#id_token=" + idToken.getSignedToken(signPrivateKey.getKey(), signPrivateKey.getId(), signPrivateKey.getAlgorithm());
    if (!StringUtil.isEmpty(state)) {
        location += "&state=" + state;
    }
    return Response.status(ResourceException.FOUND).header("Location", location).build();
}
Also used : IdToken(com.yahoo.athenz.auth.token.IdToken) IdTokenRequest(com.yahoo.athenz.zts.token.IdTokenRequest) DataCache(com.yahoo.athenz.zts.cache.DataCache) SimplePrincipal(com.yahoo.athenz.auth.impl.SimplePrincipal)

Example 2 with IdToken

use of com.yahoo.athenz.auth.token.IdToken in project athenz by yahoo.

the class ZTSClientTokenCacher method extractIdTokenServiceName.

private static String extractIdTokenServiceName(final String token) {
    if (token == null) {
        return null;
    }
    final String tokenWithoutSignature = removeSignature(token);
    IdToken idToken;
    try {
        idToken = new IdToken(tokenWithoutSignature, (JwtsSigningKeyResolver) null);
    } catch (Exception ex) {
        LOG.error("ZTSTokenCache: unable to parse id token", ex);
        return null;
    }
    final String fullServiceName = idToken.getAudience();
    if (fullServiceName == null) {
        LOG.error("ZTSTokenCache: token has no audience");
        return null;
    }
    int index = fullServiceName.lastIndexOf('.');
    if (index == -1) {
        LOG.error("ZTSTokenCache: invalid id token audience - {}", fullServiceName);
        return null;
    }
    return fullServiceName.substring(index + 1);
}
Also used : IdToken(com.yahoo.athenz.auth.token.IdToken) JwtsSigningKeyResolver(com.yahoo.athenz.auth.token.jwts.JwtsSigningKeyResolver)

Example 3 with IdToken

use of com.yahoo.athenz.auth.token.IdToken in project athenz by yahoo.

the class ZTSImpl method postAccessTokenRequest.

@Override
public AccessTokenResponse postAccessTokenRequest(ResourceContext ctx, String request) {
    final String caller = ctx.getApiName();
    final String principalDomain = logPrincipalAndGetDomain(ctx);
    validateRequest(ctx.request(), principalDomain, caller);
    // get our principal's name
    final Principal principal = ((RsrcCtxWrapper) ctx).principal();
    String principalName = principal.getFullName();
    if (StringUtil.isEmpty(request)) {
        throw requestError("Empty request body", caller, ZTSConsts.ZTS_UNKNOWN_DOMAIN, principalDomain);
    }
    // we want to log the request body in our access log so
    // we know what is the client asking for but we'll just
    // limit the request up to 1K
    ctx.request().setAttribute(ACCESS_LOG_ADDL_QUERY, getQueryLogData(request));
    // decode and store the attributes that could exist in our
    // request body
    String grantType = null;
    String scope = null;
    String proxyForPrincipal = null;
    String authzDetails = null;
    List<String> proxyPrincipalsSpiffeUris = null;
    int expiryTime = 0;
    boolean useOpenIDIssuer = false;
    String[] comps = request.split("&");
    for (String comp : comps) {
        int idx = comp.indexOf('=');
        if (idx == -1) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("postAccessTokenRequest: skipping invalid component: {}", comp);
            }
            continue;
        }
        final String key = decodeString(comp.substring(0, idx));
        if (key == null) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("postAccessTokenRequest: skipping invalid component: {}", comp);
            }
            continue;
        }
        final String value = decodeString(comp.substring(idx + 1));
        if (value == null) {
            continue;
        }
        switch(key) {
            case KEY_GRANT_TYPE:
                grantType = value.toLowerCase();
                break;
            case KEY_SCOPE:
                scope = value.toLowerCase();
                break;
            case KEY_EXPIRES_IN:
                expiryTime = ZTSUtils.parseInt(value, 0);
                break;
            case KEY_PROXY_FOR_PRINCIPAL:
                proxyForPrincipal = getProxyForPrincipalValue(value.toLowerCase(), principalName, principalDomain, caller);
                break;
            case KEY_AUTHORIZATION_DETAILS:
                authzDetails = value;
                break;
            case KEY_PROXY_PRINCIPAL_SPIFFE_URIS:
                proxyPrincipalsSpiffeUris = getProxyPrincipalSpiffeUris(value.toLowerCase(), principalDomain, caller);
                break;
            case KEY_OPENID_ISSUER:
                useOpenIDIssuer = Boolean.parseBoolean(value);
                break;
        }
    }
    if (!OAUTH_GRANT_CREDENTIALS.equals(grantType)) {
        throw requestError("Invalid grant request: " + grantType, caller, principal.getDomain(), principalDomain);
    }
    if (scope == null || scope.isEmpty()) {
        throw requestError("Invalid request: no scope provided", caller, principal.getDomain(), principalDomain);
    }
    if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("postAccessTokenRequest(principal: {}, grant-type: {}, scope: {}, expires-in: {}, proxy-for-principal: {})", principalName, grantType, scope, expiryTime, proxyForPrincipal);
    }
    // our scopes are space separated list of values
    AccessTokenRequest tokenRequest = new AccessTokenRequest(scope);
    // before using any of our values let's validate that they
    // match our schema
    final String domainName = tokenRequest.getDomainName();
    setRequestDomain(ctx, domainName);
    validate(domainName, TYPE_DOMAIN_NAME, principalDomain, caller);
    String[] requestedRoles = tokenRequest.getRoleNames();
    if (requestedRoles != null) {
        for (String requestedRole : requestedRoles) {
            validate(requestedRole, TYPE_ENTITY_NAME, principalDomain, caller);
        }
    }
    // first retrieve our domain data object from the cache
    DataCache data = dataStore.getDataCache(domainName);
    if (data == null) {
        setRequestDomain(ctx, ZTSConsts.ZTS_UNKNOWN_DOMAIN);
        throw notFoundError("No such domain: " + domainName, caller, ZTSConsts.ZTS_UNKNOWN_DOMAIN, principalDomain);
    }
    // if we're given authorization details to be included in the
    // token then we must have only role requested and we need
    // to make sure the requested fields are valid according
    // to our configured authorization details entity for the role
    validateAuthorizationDetails(authzDetails, requestedRoles, data, caller, domainName, principalDomain);
    // check if the authorized service domain matches to the
    // requested domain name
    checkRoleTokenAuthorizedServiceRequest(principal, domainName, caller);
    // process our request and retrieve the roles for the principal
    Set<String> roles = new HashSet<>();
    dataStore.getAccessibleRoles(data, domainName, principalName, requestedRoles, roles, false);
    if (roles.isEmpty()) {
        throw forbiddenError(tokenErrorMessage(caller, principalName, domainName, requestedRoles), caller, domainName, principalDomain);
    }
    // if this is proxy for operation then we want to make sure that
    // both principals have access to the same set of roles so we'll
    // remove any roles that are authorized by only one of the principals
    String proxyUser = null;
    if (proxyForPrincipal != null) {
        if (tokenRequest.isOpenIdScope()) {
            throw requestError("Proxy Principal cannot request id tokens", caller, domainName, principalDomain);
        }
        // process the role lookup for the proxy principal
        Set<String> rolesForProxy = new HashSet<>();
        dataStore.getAccessibleRoles(data, domainName, proxyForPrincipal, requestedRoles, rolesForProxy, false);
        roles.retainAll(rolesForProxy);
        if (roles.isEmpty()) {
            throw forbiddenError(tokenErrorMessage(caller, proxyForPrincipal, domainName, requestedRoles), caller, domainName, principalDomain);
        }
        // we need to switch our principal and proxy for user
        proxyUser = principalName;
        principalName = proxyForPrincipal;
    }
    if (!isPrincipalRoleCertificateAccessValid(principal, domainName, roles)) {
        throw forbiddenError("Role based Principal does not include all roles", caller, domainName, principalDomain);
    }
    long tokenTimeout = determineTokenTimeout(data, roles, null, expiryTime);
    long iat = System.currentTimeMillis() / 1000;
    AccessToken accessToken = new AccessToken();
    accessToken.setVersion(1);
    accessToken.setJwtId(UUID.randomUUID().toString());
    accessToken.setAudience(domainName);
    accessToken.setClientId(principalName);
    accessToken.setIssueTime(iat);
    accessToken.setAuthTime(iat);
    accessToken.setExpiryTime(iat + tokenTimeout);
    accessToken.setUserId(principalName);
    accessToken.setSubject(principalName);
    accessToken.setIssuer(useOpenIDIssuer ? ztsOpenIDIssuer : ztsOAuthIssuer);
    accessToken.setProxyPrincipal(proxyUser);
    accessToken.setScope(new ArrayList<>(roles));
    accessToken.setAuthorizationDetails(authzDetails);
    // if we have a certificate used for mTLS authentication then
    // we're going to bind the certificate to the access token
    // and the optional proxy principals if specified
    X509Certificate cert = principal.getX509Certificate();
    if (cert != null) {
        accessToken.setConfirmX509CertHash(cert);
        if (proxyPrincipalsSpiffeUris != null) {
            accessToken.setConfirmProxyPrincipalSpiffeUris(proxyPrincipalsSpiffeUris);
        }
    }
    String accessJwts = accessToken.getSignedToken(privateKey.getKey(), privateKey.getId(), privateKey.getAlgorithm());
    // now let's check to see if we need to create openid token
    String idJwts = null;
    if (tokenRequest.isOpenIdScope()) {
        final String serviceName = tokenRequest.getServiceName();
        validate(serviceName, TYPE_SIMPLE_NAME, principalDomain, caller);
        IdToken idToken = new IdToken();
        idToken.setVersion(1);
        idToken.setAudience(tokenRequest.getDomainName() + "." + serviceName);
        idToken.setSubject(principalName);
        idToken.setIssuer(useOpenIDIssuer ? ztsOpenIDIssuer : ztsOAuthIssuer);
        // id tokens are only valid for up to 12 hours max
        // (value configured as a system property).
        // we'll use the user specified timeout unless it's
        // over the configured max
        idToken.setIssueTime(iat);
        idToken.setAuthTime(iat);
        idToken.setExpiryTime(iat + determineIdTokenTimeout(tokenTimeout));
        idJwts = idToken.getSignedToken(privateKey.getKey(), privateKey.getId(), privateKey.getAlgorithm());
    }
    AccessTokenResponse response = new AccessTokenResponse().setAccess_token(accessJwts).setToken_type(OAUTH_BEARER_TOKEN).setExpires_in((int) tokenTimeout).setId_token(idJwts);
    if (tokenRequest.sendScopeResponse() || requestedRoles != null && requestedRoles.length != roles.size()) {
        List<String> domainRoles = new ArrayList<>();
        for (String role : roles) {
            domainRoles.add(domainName + AccessTokenRequest.OBJECT_ROLE + role);
        }
        if (tokenRequest.isOpenIdScope()) {
            domainRoles.add(AccessTokenRequest.OBJECT_OPENID);
        }
        response.setScope(String.join(" ", domainRoles));
    }
    return response;
}
Also used : IdToken(com.yahoo.athenz.auth.token.IdToken) DataCache(com.yahoo.athenz.zts.cache.DataCache) X509Certificate(java.security.cert.X509Certificate) AccessToken(com.yahoo.athenz.auth.token.AccessToken) AccessTokenRequest(com.yahoo.athenz.zts.token.AccessTokenRequest) SimplePrincipal(com.yahoo.athenz.auth.impl.SimplePrincipal)

Aggregations

IdToken (com.yahoo.athenz.auth.token.IdToken)3 SimplePrincipal (com.yahoo.athenz.auth.impl.SimplePrincipal)2 DataCache (com.yahoo.athenz.zts.cache.DataCache)2 AccessToken (com.yahoo.athenz.auth.token.AccessToken)1 JwtsSigningKeyResolver (com.yahoo.athenz.auth.token.jwts.JwtsSigningKeyResolver)1 AccessTokenRequest (com.yahoo.athenz.zts.token.AccessTokenRequest)1 IdTokenRequest (com.yahoo.athenz.zts.token.IdTokenRequest)1 X509Certificate (java.security.cert.X509Certificate)1