001package com.nimbusds.oauth2.sdk.tokenexchange;
002
003
004import java.util.*;
005
006import net.jcip.annotations.Immutable;
007
008import com.nimbusds.oauth2.sdk.AuthorizationGrant;
009import com.nimbusds.oauth2.sdk.GrantType;
010import com.nimbusds.oauth2.sdk.OAuth2Error;
011import com.nimbusds.oauth2.sdk.ParseException;
012import com.nimbusds.oauth2.sdk.id.Audience;
013import com.nimbusds.oauth2.sdk.token.Token;
014import com.nimbusds.oauth2.sdk.token.TokenTypeURI;
015import com.nimbusds.oauth2.sdk.token.TypelessToken;
016import com.nimbusds.oauth2.sdk.util.CollectionUtils;
017import com.nimbusds.oauth2.sdk.util.MultivaluedMapUtils;
018import com.nimbusds.oauth2.sdk.util.StringUtils;
019
020
021/**
022 * OAuth 2.0 token exchange grant.
023 *
024 * <p>Related specifications:
025 *
026 * <ul>
027 *     <li>OAuth 2.0 Token Exchange (RFC 8693).
028 * </ul>
029 */
030@Immutable
031public class TokenExchangeGrant extends AuthorizationGrant {
032        
033        
034        /**
035         * The grant type.
036         */
037        public static final GrantType GRANT_TYPE = GrantType.TOKEN_EXCHANGE;
038        
039        
040        /**
041         * The subject token representing the identity of the party on behalf
042         * of whom the request is being made.
043         */
044        private final Token subjectToken;
045        
046        
047        /**
048         * Identifier for the type of the subject token.
049         */
050        private final TokenTypeURI subjectTokenType;
051        
052        
053        /**
054         * Optional token representing the identity of the acting party.
055         */
056        private final Token actorToken;
057        
058        
059        /**
060         * Identifier for the type of the actor token, if present.
061         */
062        private final TokenTypeURI actorTokenType;
063        
064        
065        /**
066         * Optional identifier for the requested type of security token.
067         */
068        private final TokenTypeURI requestedTokenType;
069        
070        
071        /**
072         * Optional audience for the requested security token.
073         */
074        private final List<Audience> audience;
075        
076        
077        /**
078         * Creates a new token exchange grant.
079         *
080         * @param subjectToken     The subject token representing the identity
081         *                         of the party on behalf of whom the request
082         *                         is being made. Must not be {@code null}.
083         * @param subjectTokenType Identifier for the type of the subject
084         *                         token. Must not be {@code null}.
085         */
086        public TokenExchangeGrant(final Token subjectToken,
087                                  final TokenTypeURI subjectTokenType) {
088                
089                this(subjectToken, subjectTokenType, null, null, null, null);
090        }
091        
092        
093        /**
094         * Creates a new token exchange grant.
095         *
096         * @param subjectToken       The subject token representing the
097         *                           identity of the party on behalf of whom
098         *                           the request is being made. Must not be
099         *                           {@code null}.
100         * @param subjectTokenType   Identifier for the type of the subject
101         *                           token. Must not be {@code null}.
102         * @param actorToken         Optional token representing the identity
103         *                           of the acting party, {@code null} if not
104         *                           specified.
105         * @param actorTokenType     Identifier for the type of the actor
106         *                           token, if present.
107         * @param requestedTokenType Optional identifier for the requested type
108         *                           of security token, {@code null} if not
109         *                           specified.
110         * @param audience           Optional audience for the requested
111         *                           security token, {@code null} if not
112         *                           specified.
113         */
114        public TokenExchangeGrant(final Token subjectToken,
115                                  final TokenTypeURI subjectTokenType,
116                                  final Token actorToken,
117                                  final TokenTypeURI actorTokenType,
118                                  final TokenTypeURI requestedTokenType,
119                                  final List<Audience> audience) {
120                
121                super(GRANT_TYPE);
122                
123                if (subjectToken == null) {
124                        throw new IllegalArgumentException("The subject token must not be null");
125                }
126                this.subjectToken = subjectToken;
127                
128                if (subjectTokenType == null) {
129                        throw new IllegalArgumentException("The subject token type must not be null");
130                }
131                this.subjectTokenType = subjectTokenType;
132                
133                this.actorToken = actorToken;
134                
135                if (actorToken != null && actorTokenType == null) {
136                        throw new IllegalArgumentException("If an actor token is specified the actor token type must not be null");
137                }
138                this.actorTokenType = actorTokenType;
139                
140                this.requestedTokenType = requestedTokenType;
141                
142                this.audience = audience;
143        }
144        
145        
146        /**
147         * Returns the subject token representing the identity of the party on
148         * behalf of whom the request is being made.
149         *
150         * @return The subject token, {@code null} if not specified.
151         */
152        public Token getSubjectToken() {
153                
154                return subjectToken;
155        }
156        
157        
158        /**
159         * Returns the identifier for the type of the subject token.
160         *
161         * @return The subject token type identifier.
162         */
163        public TokenTypeURI getSubjectTokenType() {
164                
165                return subjectTokenType;
166        }
167        
168        
169        /**
170         * Returns the optional token representing the identity of the acting
171         * party.
172         *
173         * @return The actor token, {@code null} if not specified.
174         */
175        public Token getActorToken() {
176                
177                return actorToken;
178        }
179        
180        
181        /**
182         * Returns the identifier for the type of the optional actor token, if
183         * present.
184         *
185         * @return The actor token type identifier, {@code null} if not
186         *         present.
187         */
188        public TokenTypeURI getActorTokenType() {
189                
190                return actorTokenType;
191        }
192        
193        
194        /**
195         * Returns the optional identifier for the requested type of security
196         * token.
197         *
198         * @return The requested token type, {@code null} if not specified.
199         */
200        public TokenTypeURI getRequestedTokenType() {
201                
202                return requestedTokenType;
203        }
204        
205        
206        /**
207         * Returns the optional audience for the requested security token.
208         *
209         * @return The audience, {@code null} if not specified.
210         */
211        public List<Audience> getAudience() {
212                
213                return audience;
214        }
215        
216        
217        @Override
218        public Map<String, List<String>> toParameters() {
219                
220                Map<String, List<String>> params = new LinkedHashMap<>();
221                
222                params.put("grant_type", Collections.singletonList(GRANT_TYPE.getValue()));
223                
224                if (CollectionUtils.isNotEmpty(audience)) {
225                        params.put("audience", Audience.toStringList(audience));
226                }
227                
228                if (requestedTokenType != null) {
229                        params.put("requested_token_type", Collections.singletonList(requestedTokenType.getURI().toString()));
230                }
231                
232                params.put("subject_token", Collections.singletonList(subjectToken.getValue()));
233                params.put("subject_token_type", Collections.singletonList(subjectTokenType.getURI().toString()));
234                
235                if (actorToken != null) {
236                        params.put("actor_token", Collections.singletonList(actorToken.getValue()));
237                        params.put("actor_token_type", Collections.singletonList(actorTokenType.getURI().toString()));
238                }
239                
240                return params;
241        }
242        
243        
244        private static List<Audience> parseAudience(final Map<String, List<String>> params) {
245                
246                List<String> audienceList = params.get("audience");
247                
248                if (CollectionUtils.isEmpty(audienceList)) {
249                        return null;
250                }
251                
252                return Audience.create(audienceList);
253        }
254        
255        
256        private static TokenTypeURI parseTokenType(final Map<String, List<String>> params, final String key, final boolean mandatory)
257                throws ParseException {
258                
259                String tokenTypeString = MultivaluedMapUtils.getFirstValue(params, key);
260                
261                if (StringUtils.isBlank(tokenTypeString)) {
262                        if (mandatory) {
263                                String msg = String.format("Missing or empty %s parameter", key);
264                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg));
265                        } else {
266                                return null;
267                        }
268                }
269                
270                try {
271                        return TokenTypeURI.parse(tokenTypeString);
272                } catch (ParseException uriSyntaxException) {
273                        String msg = "Invalid " + key + " " + tokenTypeString;
274                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg));
275                }
276        }
277        
278        
279        private static TypelessToken parseToken(final Map<String, List<String>> params, final String key, final boolean mandatory)
280                throws ParseException {
281                
282                String tokenString = MultivaluedMapUtils.getFirstValue(params, key);
283                
284                if (StringUtils.isBlank(tokenString)) {
285                        
286                        if (mandatory) {
287                                String msg = String.format("Missing or empty %s parameter", key);
288                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg));
289                        } else {
290                                return null;
291                        }
292                }
293                
294                return new TypelessToken(tokenString);
295        }
296        
297        
298        /**
299         * Parses a token exchange grant from the specified request body
300         * parameters.
301         *
302         * <p>Example:
303         *
304         * <pre>
305         * grant_type=urn:ietf:params:oauth:grant-type:token-exchange
306         * resource=https://backend.example.com/api
307         * subject_token=accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC
308         * subject_token_type=urn:ietf:params:oauth:token-type:access_token
309         * </pre>
310         *
311         * @param params The parameters.
312         *
313         * @return The token exchange grant.
314         *
315         * @throws ParseException If parsing failed.
316         */
317        public static TokenExchangeGrant parse(final Map<String, List<String>> params)
318                throws ParseException {
319                
320                GrantType.ensure(GRANT_TYPE, params);
321                
322                List<Audience> audience = parseAudience(params);
323                TokenTypeURI requestedTokenType = parseTokenType(params, "requested_token_type", false);
324                TypelessToken subjectToken = parseToken(params, "subject_token", true);
325                TokenTypeURI subjectTokenType = parseTokenType(params, "subject_token_type", true);
326                TypelessToken actorToken = parseToken(params, "actor_token", false);
327                TokenTypeURI actorTokenType = parseTokenType(params, "actor_token_type", false);
328                
329                return new TokenExchangeGrant(subjectToken, subjectTokenType, actorToken, actorTokenType, requestedTokenType, audience);
330        }
331        
332        
333        @Override
334        public boolean equals(Object o) {
335                if (this == o) return true;
336                if (!(o instanceof TokenExchangeGrant)) return false;
337                TokenExchangeGrant that = (TokenExchangeGrant) o;
338                return requestedTokenType.equals(that.requestedTokenType) &&
339                        subjectToken.equals(that.subjectToken) &&
340                        subjectTokenType.equals(that.subjectTokenType) &&
341                        Objects.equals(actorToken, that.actorToken) &&
342                        Objects.equals(actorTokenType, that.actorTokenType);
343        }
344        
345        
346        @Override
347        public int hashCode() {
348                return Objects.hash(requestedTokenType, subjectToken, subjectTokenType, actorToken, actorTokenType);
349        }
350}