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}