001package com.nimbusds.openid.connect.provider.spi.tokens;
002
003
004import java.util.Date;
005import java.util.List;
006import java.util.Map;
007
008import com.nimbusds.jwt.JWTClaimsSet;
009import com.nimbusds.oauth2.sdk.auth.X509CertificateConfirmation;
010import com.nimbusds.oauth2.sdk.id.*;
011import net.jcip.annotations.ThreadSafe;
012import net.minidev.json.JSONObject;
013
014
015/**
016 * Base implementation of the SPI for encoding and decoding authorisations for
017 * self-contained access tokens into JWT claims sets.
018 *
019 * <p>Provides encoding and decoding for all token parameters for which there
020 * is an appropriate standard JWT claim (see JSON Web Token (JWT) (RFC 7519),
021 * section 4.1, draft-ietf-oauth-mtls-07):
022 *
023 * <ul>
024 *     <li>subject - "sub"
025 *     <li>actor - "act"
026 *     <li>expiration time - "exp"
027 *     <li>issue time - "iat"
028 *     <li>audience - "aud"
029 *     <li>JWT ID - "jti"
030 *     <li>client X.509 certificate SHA-256 thumbprint (mTLS) - "cnf.x5t#S256"
031 * </ul>
032 *
033 * <p>The extending class should implement encoding and decoding for the
034 * remaining token parameters:
035 *
036 * <ul>
037 *     <li>client ID
038 *     <li>scope
039 *     <li>consented OpenID claim names
040 *     <li>preferred claims locales
041 *     <li>preset OpenID claims
042 *     <li>additional data
043 * </ul>
044 */
045@ThreadSafe
046public abstract class BaseSelfContainedAccessTokenClaimsCodec implements SelfContainedAccessTokenClaimsCodec {
047        
048        
049        @Override
050        public JWTClaimsSet encode(final AccessTokenAuthorization tokenAuthz, final TokenEncoderContext context) {
051                
052                JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder();
053                
054                if (tokenAuthz.getSubject()        != null) builder.subject(tokenAuthz.getSubject().getValue());
055                if (tokenAuthz.getActor()          != null) builder.claim("act", tokenAuthz.getActor().toJSONObject());
056                if (tokenAuthz.getExpirationTime() != null) builder.expirationTime(Date.from(tokenAuthz.getExpirationTime()));
057                if (tokenAuthz.getIssueTime()      != null) builder.issueTime(Date.from(tokenAuthz.getIssueTime()));
058                if (tokenAuthz.getIssuer()         != null) builder.issuer(tokenAuthz.getIssuer().getValue());
059                if (tokenAuthz.getAudienceList()   != null) builder.audience(Audience.toStringList(tokenAuthz.getAudienceList()));
060                if (tokenAuthz.getJWTID()          != null) builder.jwtID(tokenAuthz.getJWTID().getValue());
061                if (tokenAuthz.getClientCertificateConfirmation() != null) {
062                        Map.Entry<String, JSONObject> cnf = tokenAuthz.getClientCertificateConfirmation().toJWTClaim();
063                        builder.claim(cnf.getKey(), cnf.getValue());
064                }
065                
066                return builder.build();
067        }
068        
069        
070        @Override
071        public AccessTokenAuthorization decode(final JWTClaimsSet claimsSet, final TokenCodecContext context)
072                throws TokenDecodeException {
073                
074                MutableAccessTokenAuthorization authz = new MutableAccessTokenAuthorization();
075                
076                String sub = claimsSet.getSubject();
077                if (sub != null) authz.withSubject(new Subject(sub));
078                
079                try {
080                        JSONObject act = claimsSet.getJSONObjectClaim("act");
081                        if (act != null) authz.withActor(Actor.parse(act));
082                } catch (Exception e) {
083                        throw new TokenDecodeException("Couldn't parse actor: " + e.getMessage(), e);
084                }
085                
086                Date exp = claimsSet.getExpirationTime();
087                if (exp != null) authz.withExpirationTime(exp.toInstant());
088                
089                Date iat = claimsSet.getIssueTime();
090                if (iat != null) authz.withIssueTime(iat.toInstant());
091                
092                String iss = claimsSet.getIssuer();
093                if (iss != null) authz.withIssuer(new Issuer(iss));
094                
095                List<String> aud = claimsSet.getAudience();
096                if (aud != null && ! aud.isEmpty()) authz.withAudienceList(Audience.create(aud));
097                
098                String jti = claimsSet.getJWTID();
099                if (jti != null) authz.withJWTID(new JWTID(jti));
100                
101                authz.withClientCertificateConfirmation(X509CertificateConfirmation.parse(claimsSet));
102                
103                return authz;
104        }
105}