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}