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-16), 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#getClaimsData() claims fullfilment data} 051 * <li>{@link AccessTokenAuthorization#getPresetClaims() preset OpenID claims} 052 * <li>{@link AccessTokenAuthorization#getSubjectSessionKey() subject session key} 053 * <li>{@link AccessTokenAuthorization#getActor() actor, in impersonation and delegation scenarios} 054 * <li>{@link AccessTokenAuthorization#getData() additional data} 055 * <li>{@link AccessTokenAuthorization#getOtherTopLevelParameters() custom top-level parameters} 056 * </ul> 057 * 058 * <p>The extending class may implement output of the above non-standard 059 * parameters. It may also choose not to output parameters if they are not 060 * required by the client (resource server), e.g. for privacy and data 061 * minimisation purposes. 062 */ 063@ThreadSafe 064public abstract class BaseTokenIntrospectionResponseComposer implements TokenIntrospectionResponseComposer { 065 066 067 @Override 068 public TokenIntrospectionSuccessResponse compose(final AccessTokenAuthorization tokenAuthz, 069 final TokenIntrospectionContext context) { 070 071 if (tokenAuthz == null) { 072 // Access token was found invalid or expired 073 return new TokenIntrospectionSuccessResponse.Builder(false) 074 .build(); 075 } 076 077 AccessTokenType tokenType = tokenAuthz.getJWKThumbprintConfirmation() != null ? 078 AccessTokenType.DPOP : AccessTokenType.BEARER; 079 080 081 TokenIntrospectionSuccessResponse.Builder builder = new TokenIntrospectionSuccessResponse.Builder(true) 082 .tokenType(tokenType) 083 .subject(tokenAuthz.getSubject()) 084 .clientID(tokenAuthz.getClientID()) 085 .scope(tokenAuthz.getScope()) 086 .expirationTime(tokenAuthz.getExpirationTime() != null ? Date.from(tokenAuthz.getExpirationTime()) : null) 087 .issueTime(tokenAuthz.getIssueTime() != null ? Date.from(tokenAuthz.getIssueTime()) : null) 088 .issuer(tokenAuthz.getIssuer()) 089 .audience(tokenAuthz.getAudienceList()) 090 .jwtID(tokenAuthz.getJWTID()); 091 092 if (tokenAuthz.getClientCertificateConfirmation() != null) { 093 builder = builder.x509CertificateConfirmation(tokenAuthz.getClientCertificateConfirmation()); 094 } 095 096 if (tokenAuthz.getJWKThumbprintConfirmation() != null) { 097 builder = builder.jwkThumbprintConfirmation(tokenAuthz.getJWKThumbprintConfirmation()); 098 } 099 100 return builder.build(); 101 } 102}