001package com.nimbusds.openid.connect.provider.spi.tokens.introspection;
002
003
004import java.sql.Date;
005
006import net.jcip.annotations.ThreadSafe;
007
008import com.nimbusds.oauth2.sdk.TokenIntrospectionSuccessResponse;
009import com.nimbusds.oauth2.sdk.token.AccessTokenType;
010import com.nimbusds.openid.connect.provider.spi.tokens.AccessTokenAuthorization;
011
012
013/**
014 * Base implementation of the SPI for composing token introspection (RFC 7662)
015 * responses.
016 *
017 * <p>Outputs the introspection details specified in:
018 *
019 * <ul>
020 *    <li>OAuth 2.0 Token Introspection (RFC 7662), section 2.2;
021 *    <li>OAuth 2.0 Mutual TLS Client Authentication and Certificate Bound
022 *        Access Tokens (RFC 8705), section 3.2;
023 *    <li>OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer
024 *        (DPoP) (draft-ietf-oauth-dpop-03), section 6.
025 * </ul>
026 *
027 * <p>Parameters:
028 *
029 * <ul>
030 *     <li>"active"
031 *     <li>"scope"
032 *     <li>"client_id"
033 *     <li>"token_type"
034 *     <li>"exp"
035 *     <li>"iat"
036 *     <li>"sub"
037 *     <li>"aud"
038 *     <li>"iss"
039 *     <li>"jti"
040 *     <li>"cnf.x5t#S256"
041 *     <li>"cnf.jkt"
042 * </ul>
043 *
044 * <p>The following non-standard access token parameters are not output by this
045 * base implementation:
046 *
047 * <ul>
048 *     <li>{@link AccessTokenAuthorization#getClaimNames() consented OpenID claim names}
049 *     <li>{@link AccessTokenAuthorization#getClaimsLocales() preferred claims locales}
050 *     <li>{@link AccessTokenAuthorization#getPresetClaims() preset OpenID claims}
051 *     <li>{@link AccessTokenAuthorization#getActor() actor, in impersonation and delegation scenarios}
052 *     <li>{@link AccessTokenAuthorization#getData() additional data}
053 * </ul>
054 *
055 * <p>The extending class may implement output of the above non-standard
056 * parameters. It may also choose not to output parameters if they are not
057 * required by the client (resource server), e.g. for privacy and data
058 * minimisation purposes.
059 */
060@ThreadSafe
061public abstract class BaseTokenIntrospectionResponseComposer implements TokenIntrospectionResponseComposer {
062        
063        
064        @Override
065        public TokenIntrospectionSuccessResponse compose(final AccessTokenAuthorization tokenAuthz,
066                                                         final TokenIntrospectionContext context) {
067                
068                if (tokenAuthz == null) {
069                        // Access token was found invalid or expired
070                        return new TokenIntrospectionSuccessResponse.Builder(false)
071                                .build();
072                }
073                
074                AccessTokenType tokenType = tokenAuthz.getJWKThumbprintConfirmation() != null ?
075                        AccessTokenType.DPOP : AccessTokenType.BEARER;
076                
077                
078                TokenIntrospectionSuccessResponse.Builder builder = new TokenIntrospectionSuccessResponse.Builder(true)
079                        .tokenType(tokenType)
080                        .subject(tokenAuthz.getSubject())
081                        .clientID(tokenAuthz.getClientID())
082                        .scope(tokenAuthz.getScope())
083                        .expirationTime(tokenAuthz.getExpirationTime() != null ? Date.from(tokenAuthz.getExpirationTime()) : null)
084                        .issueTime(tokenAuthz.getIssueTime() != null ? Date.from(tokenAuthz.getIssueTime()) : null)
085                        .issuer(tokenAuthz.getIssuer())
086                        .audience(tokenAuthz.getAudienceList())
087                        .jwtID(tokenAuthz.getJWTID());
088                
089                if (tokenAuthz.getClientCertificateConfirmation() != null) {
090                        builder = builder.x509CertificateConfirmation(tokenAuthz.getClientCertificateConfirmation());
091                }
092                
093                if (tokenAuthz.getJWKThumbprintConfirmation() != null) {
094                        builder = builder.jwkThumbprintConfirmation(tokenAuthz.getJWKThumbprintConfirmation());
095                }
096                
097                return builder.build();
098        }
099}