001package com.nimbusds.openid.connect.provider.spi.grants; 002 003 004import com.nimbusds.oauth2.sdk.ParseException; 005import com.nimbusds.oauth2.sdk.Scope; 006import com.nimbusds.oauth2.sdk.id.Subject; 007import com.nimbusds.oauth2.sdk.token.TokenTypeURI; 008import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 009import net.jcip.annotations.Immutable; 010import net.minidev.json.JSONObject; 011import org.checkerframework.checker.nullness.qual.Nullable; 012 013import java.util.Objects; 014 015 016/** 017 * Authorisation produced by a {@link TokenExchangeGrantHandler}. 018 * 019 * <p>Supported issued token types: 020 * 021 * <ul> 022 * <li>Access token ({@link TokenTypeURI#ACCESS_TOKEN}) 023 * </ul> 024 */ 025@Immutable 026public class TokenExchangeAuthorization extends SubjectAuthorization { 027 028 029 /** 030 * The type of the issued token. 031 */ 032 private final TokenTypeURI tokenType = TokenTypeURI.ACCESS_TOKEN; 033 034 035 /** 036 * Controls the authorisation lifetime, {@code true} for a long-lived 037 * (implies persistence), {@code false} for a short-lived (transient). 038 */ 039 private final boolean longLived; 040 041 042 /** 043 * The refresh token specification. 044 */ 045 private final RefreshTokenSpec refreshTokenSpec; 046 047 048 /** 049 * Creates a new token exchange authorisation specifying the issue of 050 * an own (local) {@link TokenTypeURI#ACCESS_TOKEN access token}. 051 * 052 * @param subject The subject (end-user) identifier. Must not 053 * be {@code null}. 054 * @param scope The authorised scope. Must not be 055 * {@code null}. 056 * @param accessTokenSpec The access token specification. Must not be 057 * {@code null}. 058 * @param claimsSpec The OpenID claims specification. Must not be 059 * {@code null}. 060 * @param data Additional data as a JSON object, 061 * {@code null} if not specified. 062 */ 063 public TokenExchangeAuthorization(final Subject subject, 064 final Scope scope, 065 final AccessTokenSpec accessTokenSpec, 066 final ClaimsSpec claimsSpec, 067 final @Nullable JSONObject data) { 068 069 this(subject, scope, false, accessTokenSpec, RefreshTokenSpec.DEFAULT, claimsSpec, data); 070 } 071 072 073 /** 074 * Creates a new token exchange authorisation specifying the issue of 075 * an own (local) {@link TokenTypeURI#ACCESS_TOKEN access token} with 076 * an optional refresh token. 077 * 078 * @param subject The subject (end-user) identifier. Must not 079 * be {@code null}. 080 * @param scope The authorised scope. Must not be 081 * {@code null}. 082 * @param longLived Controls the authorisation lifetime. 083 * {@code true} for a long-lived (implies 084 * persistence), {@code false} for a 085 * short-lived (transient). 086 * @param accessTokenSpec The access token specification. Must not be 087 * {@code null}. 088 * @param refreshTokenSpec The refresh token specification. Must not be 089 * {@code null}. 090 * @param claimsSpec The OpenID claims specification. Must not be 091 * {@code null}. 092 * @param data Additional data as a JSON object, 093 * {@code null} if not specified. 094 */ 095 public TokenExchangeAuthorization(final Subject subject, 096 final Scope scope, 097 final boolean longLived, 098 final AccessTokenSpec accessTokenSpec, 099 final RefreshTokenSpec refreshTokenSpec, 100 final ClaimsSpec claimsSpec, 101 final @Nullable JSONObject data) { 102 103 this(subject, scope, longLived, accessTokenSpec, refreshTokenSpec, IDTokenSpec.NONE, claimsSpec, data); 104 } 105 106 107 /** 108 * Creates a new token exchange authorisation specifying the issue of 109 * an own (local) {@link TokenTypeURI#ACCESS_TOKEN access token} with 110 * an optional refresh token and ID token. 111 * 112 * @param subject The subject (end-user) identifier. Must not 113 * be {@code null}. 114 * @param scope The authorised scope. Must not be 115 * {@code null}. 116 * @param longLived Controls the authorisation lifetime. 117 * {@code true} for a long-lived (implies 118 * persistence), {@code false} for a 119 * short-lived (transient). 120 * @param accessTokenSpec The access token specification. Must not be 121 * {@code null}. 122 * @param refreshTokenSpec The refresh token specification. Must not be 123 * {@code null}. 124 * @param idTokenSpec The ID token specification. Must not be 125 * {@code null}. 126 * @param claimsSpec The OpenID claims specification. Must not be 127 * {@code null}. 128 * @param data Additional data as a JSON object, 129 * {@code null} if not specified. 130 */ 131 public TokenExchangeAuthorization(final Subject subject, 132 final Scope scope, 133 final boolean longLived, 134 final AccessTokenSpec accessTokenSpec, 135 final RefreshTokenSpec refreshTokenSpec, 136 final IDTokenSpec idTokenSpec, 137 final ClaimsSpec claimsSpec, 138 final @Nullable JSONObject data) { 139 140 super(subject, scope, accessTokenSpec, idTokenSpec, claimsSpec, data); 141 this.longLived = longLived; 142 this.refreshTokenSpec = Objects.requireNonNull(refreshTokenSpec); 143 } 144 145 146 /** 147 * Returns the type of the issued token. 148 * 149 * @return The type of the issued token. 150 */ 151 public TokenTypeURI getTokenType() { 152 return tokenType; 153 } 154 155 156 /** 157 * Returns the authorisation lifetime. 158 * 159 * @return {@code true} for a long-lived authorisation (implies 160 * persistence), {@code false} for a short-lived (transient). 161 */ 162 public boolean isLongLived() { 163 return longLived; 164 } 165 166 167 /** 168 * Returns the refresh token specification. 169 * 170 * @return The refresh token specification. 171 */ 172 public RefreshTokenSpec getRefreshTokenSpec() { 173 return refreshTokenSpec; 174 } 175 176 177 @Override 178 public JSONObject toJSONObject() { 179 180 JSONObject o = super.toJSONObject(); 181 o.put("long_lived", longLived); 182 o.put("issued_token_type", tokenType.toString()); 183 if (refreshTokenSpec.issue()) { 184 o.put("refresh_token", refreshTokenSpec.toJSONObject()); 185 } 186 return o; 187 } 188 189 190 /** 191 * Parses a token exchange authorisation from the specified JSON 192 * object. 193 * 194 * @param jsonObject The JSON object to parse. Must not be 195 * {@code null}. 196 * 197 * @return The token exchange authorisation. 198 * 199 * @throws ParseException If parsing failed. 200 */ 201 public static TokenExchangeAuthorization parse(final JSONObject jsonObject) 202 throws ParseException { 203 204 TokenTypeURI tokenTypeURI = TokenTypeURI.parse(JSONObjectUtils.getURI(jsonObject, "issued_token_type").toString()); 205 206 if (! TokenTypeURI.ACCESS_TOKEN.equals(tokenTypeURI)) { 207 throw new ParseException("The issued_token_type must be " + TokenTypeURI.ACCESS_TOKEN); 208 } 209 210 boolean longLived = JSONObjectUtils.getBoolean(jsonObject, "long_lived", false); 211 212 RefreshTokenSpec rtSpec; 213 if (jsonObject.containsKey("refresh_token")) { 214 rtSpec = RefreshTokenSpec.parse(JSONObjectUtils.getJSONObject(jsonObject, "refresh_token")); 215 } else { 216 rtSpec = new RefreshTokenSpec(); 217 } 218 219 SubjectAuthorization subjectAuthz = SubjectAuthorization.parse(jsonObject); 220 221 return new TokenExchangeAuthorization( 222 subjectAuthz.getSubject(), 223 subjectAuthz.getScope(), 224 longLived, 225 subjectAuthz.getAccessTokenSpec(), 226 rtSpec, 227 subjectAuthz.getIDTokenSpec(), 228 subjectAuthz.getClaimsSpec(), 229 subjectAuthz.getData()); 230 } 231 232 233 /** 234 * Parses a token exchange authorisation from the specified JSON object 235 * string. 236 * 237 * @param json The JSON object string to parse. Must not be 238 * {@code null}. 239 * 240 * @return The token exchange authorisation. 241 * 242 * @throws ParseException If parsing failed. 243 */ 244 public static TokenExchangeAuthorization parse(final String json) 245 throws ParseException { 246 247 return parse(JSONObjectUtils.parse(json)); 248 } 249}