001package com.nimbusds.openid.connect.provider.spi.grants;
002
003
004import java.util.*;
005
006import net.minidev.json.JSONObject;
007
008import com.nimbusds.langtag.LangTag;
009import com.nimbusds.langtag.LangTagException;
010import com.nimbusds.langtag.LangTagUtils;
011
012import com.nimbusds.oauth2.sdk.ParseException;
013import com.nimbusds.oauth2.sdk.Scope;
014import com.nimbusds.oauth2.sdk.id.Audience;
015import com.nimbusds.oauth2.sdk.id.Subject;
016import com.nimbusds.oauth2.sdk.token.TokenEncoding;
017import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
018
019import com.nimbusds.openid.connect.sdk.claims.ACR;
020import com.nimbusds.openid.connect.sdk.claims.AMR;
021import com.nimbusds.openid.connect.sdk.claims.ClaimsTransport;
022
023
024/**
025 * Authorisation response from a {@link PasswordGrantHandler}.
026 *
027 * <p>The minimum details it contains is the identifier of the authenticated
028 * subject (end-user) and the authorised scope values. The other parameters are
029 * optional or may have suitable defaults.
030 */
031public final class PasswordGrantAuthorization extends GrantAuthorization {
032
033
034        /**
035         * The identifier of the authorised subject.
036         */
037        private final Subject subject;
038
039
040        /**
041         * The time of the subject authentication. If {@code null} it will be
042         * set to now. Applies only if an ID token is issued.
043         */
044        private final Date authTime;
045
046
047        /**
048         * The Authentication Context Class Reference (ACR), {@code null} if
049         * not specified. Applies only if an ID token is issued.
050         */
051        private final ACR acr;
052
053
054        /**
055         * The Authentication Methods Reference (AMR) list, {@code null} if not
056         * specified. Applies only if an ID token is issued.
057         */
058        private final List<AMR> amrList;
059
060
061        /**
062         * Controls the authorisation lifetime. {@code true} for a long-lived
063         * authorisation (implies persistence), {@code false} for a short-lived
064         * one.
065         */
066        private final boolean longLived;
067
068
069        /**
070         * Controls the refresh token issue. If {@code true} a refresh token
071         * must be issued (requires a long-lived authorisation), {@code false}
072         * if only an access token is issued.
073         */
074        private final boolean issueRefreshToken;
075
076
077        /**
078         * Controls the ID token issue. If {@code true} an ID token must be
079         * issued.
080         */
081        private final boolean issueIDToken;
082
083
084        /**
085         * Authorised OpenID Connect UserInfo claims, {@code null} if none.
086         */
087        private final Set<String> claims;
088
089
090        /**
091         * The preferred claims locales, {@code null} if not specified.
092         */
093        private final List<LangTag> claimsLocales;
094
095
096        /**
097         * Additional or preset claims to be included in the ID token,
098         * {@code null} if none.
099         */
100        private final JSONObject presetIDTokenClaims;
101
102
103        /**
104         * Additional or preset claims to be included in the UserInfo response,
105         * {@code null} if none.
106         */
107        private final JSONObject presetUserInfoClaims;
108
109
110        /**
111         * The preferred claims transport, {@code null} if not specified
112         * (implies UserInfo endpoint).
113         */
114        private final ClaimsTransport claimsTransport;
115
116
117        /**
118         * Creates a new OAuth 2.0 - only authorisation response from a
119         * {@link PasswordGrantHandler}.
120         *
121         * @param subject             The identifier of the authorised
122         *                            subject. Must not be {@code null}.
123         * @param scope               The authorised scope values. Must not be
124         *                            {@code null}.
125         * @param audList             Explicit list of audiences for the access
126         *                            token, {@code null} if not specified.
127         * @param longLived           Controls the authorisation lifetime.
128         *                            {@code true} for a long-lived
129         *                            authorisation (implies persistence),
130         *                            {@code false} for a short-lived one.
131         * @param accessTokenLifetime The access token lifetime, in seconds,
132         *                            zero if not specified.
133         * @param accessTokenEncoding The access token encoding, {@code null}
134         *                            if not specified.
135         * @param issueRefreshToken   Controls the refresh token issue. If
136         *                            {@code true} a refresh token must be
137         *                            issued (requires a long-lived
138         *                            authorisation), {@code false} if only an
139         *                            access token is issued.
140         */
141        public PasswordGrantAuthorization(final Subject subject,
142                                          final Scope scope,
143                                          final List<Audience> audList,
144                                          final boolean longLived,
145                                          final long accessTokenLifetime,
146                                          final TokenEncoding accessTokenEncoding,
147                                          final boolean issueRefreshToken) {
148
149                this (subject, null, null, null, scope, audList, longLived, accessTokenLifetime, accessTokenEncoding, issueRefreshToken,
150                        false, null, null, null, null, null);
151        }
152
153
154        /**
155         * Creates a new OpenID Connect / OAuth 2.0 authorisation response from
156         * a {@link PasswordGrantHandler}.
157         *
158         * @param subject              The identifier of the authorised
159         *                             subject. Must not be {@code null}.
160         * @param authTime             The time of the subject authentication.
161         *                             If {@code null} it will be set to now.
162         *                             Applies only if an ID token is issued.
163         * @param acr                  The Authentication Context Class
164         *                             Reference (ACR), {@code null} if not
165         *                             specified. Applies only if an ID token
166         *                             is issued.
167         * @param amrList              The Authentication Methods Reference
168         *                             (AMR) list, {@code null} if not
169         *                             specified. Applies only if an ID token
170         *                             is issued.
171         * @param scope                The authorised scope values. Must not be
172         *                             {@code null}.
173         * @param audList              Explicit list of audiences for the
174         *                             access token, {@code null} if not
175         *                             specified.
176         * @param longLived            Controls the authorisation lifetime.
177         *                             {@code true} for a long-lived
178         *                             authorisation (implies persistence),
179         *                             {@code false} for a short-lived one.
180         * @param accessTokenLifetime  The access token lifetime, in seconds,
181         *                             zero if not specified.
182         * @param accessTokenEncoding  The access token encoding, {@code null}
183         *                             if not specified.
184         * @param issueRefreshToken    Controls the refresh token issue. If
185         *                             {@code true} a refresh token must be
186         *                             issued (requires a long-lived
187         *                             authorisation), {@code false} if only an
188         *                             access token is issued.
189         * @param issueIDToken         Controls the ID token issue. If
190         *                             {@code true} an ID token must be issued.
191         * @param claims               Authorised OpenID Connect UserInfo
192         *                             claims, {@code null} if none.
193         * @param claimsLocales        The preferred claims locales,
194         *                             {@code null} if not specified.
195         * @param presetIDTokenClaims  Additional or preset claims to be
196         *                             included in the ID token, {@code null}
197         *                             if none.
198         * @param presetUserInfoClaims Additional or preset claims to be
199         *                             included in the UserInfo response,
200         *                             {@code null} if none.
201         * @param claimsTransport      The preferred claims transport,
202         *                             {@code null} if not specified (implies
203         *                             UserInfo endpoint).
204         */
205        public PasswordGrantAuthorization(final Subject subject,
206                                          final Date authTime,
207                                          final ACR acr,
208                                          final List<AMR> amrList,
209                                          final Scope scope,
210                                          final List<Audience> audList,
211                                          final boolean longLived,
212                                          final long accessTokenLifetime,
213                                          final TokenEncoding accessTokenEncoding,
214                                          final boolean issueRefreshToken,
215                                          final boolean issueIDToken,
216                                          final Set<String> claims,
217                                          final List<LangTag> claimsLocales,
218                                          final JSONObject presetIDTokenClaims,
219                                          final JSONObject presetUserInfoClaims,
220                                          final ClaimsTransport claimsTransport) {
221
222                super(scope, audList, accessTokenLifetime, accessTokenEncoding);
223
224                if (subject == null)
225                        throw new IllegalArgumentException("The subject must not be null");
226
227                this.subject = subject;
228
229                this.authTime = authTime;
230                this.acr = acr;
231                this.amrList = amrList;
232                this.longLived = longLived;
233                this.issueRefreshToken = issueRefreshToken;
234                this.issueIDToken = issueIDToken;
235                this.claims = claims;
236                this.claimsLocales = claimsLocales;
237                this.presetIDTokenClaims = presetIDTokenClaims;
238                this.presetUserInfoClaims = presetUserInfoClaims;
239                this.claimsTransport = claimsTransport;
240        }
241
242
243        /**
244         * Returns the authorised subject.
245         *
246         * @return The identifier of the authorised subject.
247         */
248        public Subject getSubject() {
249                return subject;
250        }
251
252
253        /**
254         * Returns the time of the subject authentication.
255         *
256         * @return The time of the subject authentication. If {@code null} it
257         *         will be set to now. Applies only if an ID token is issued.
258         */
259        public Date getAuthTime() {
260                return authTime;
261        }
262
263
264        /**
265         * Returns the Authentication Context Class Reference (ACR).
266         *
267         * @return The Authentication Context Class Reference (ACR),
268         *         {@code null} if not specified. Applies only if an ID token
269         *         is issued.
270         */
271        public ACR getACR() {
272                return acr;
273        }
274
275
276        /**
277         * Returns The Authentication Methods Reference (AMR) list.
278         *
279         * @return The Authentication Methods Reference (AMR) list,
280         *         {@code null} if not specified. Applies only if an ID token
281         *         is issued.
282         */
283        public List<AMR> getAMRList() {
284                return amrList;
285        }
286
287
288        /**
289         * Returns the authorisation lifetime.
290         *
291         * @return {@code true} for a long-lived authorisation (implies
292         *         persistence), {@code false} for a short-lived one.
293         */
294        public boolean isLongLived() {
295                return longLived;
296        }
297
298
299        /**
300         * Returns the refresh token issue policy.
301         *
302         * @return {@code true} if refresh token issue is allowed (requires a
303         *         long-lived authorisation), else not.
304         */
305        public boolean allowsRefreshTokenIssue() {
306                return issueRefreshToken;
307        }
308
309
310        /**
311         * Returns the ID token issue policy.
312         *
313         * @return {@code true} to issue an ID token, else not.
314         */
315        public boolean issueIDToken() {
316                return issueIDToken;
317        }
318
319
320        /**
321         * Returns the authorised OpenID Connect UserInfo claims.
322         *
323         * @return The authorised OpenID Connect UserInfo claims, {@code null}
324         *         if none.
325         */
326        public Set<String> getClaims() {
327                return claims;
328        }
329
330
331        /**
332         * Returns the preferred OpenID Connect claims locales.
333         *
334         * @return The preferred OpenID Connect claims locales, {@code null} if
335         *         not specified.
336         */
337        public List<LangTag> getClaimsLocales() {
338
339                return claimsLocales;
340        }
341
342
343        /**
344         * Returns the additional or preset claims to be included in the ID
345         * token.
346         *
347         * @return The additional or preset claims to be included in the ID
348         *         token, {@code null} if none.
349         */
350        public JSONObject getPresetIDTokenClaims() {
351                return presetIDTokenClaims;
352        }
353
354
355        /**
356         * Returns the additional or preset claims to be included in the
357         * UserInfo response.
358         *
359         * @return The additional or preset claims to be included in the
360         *         UserInfo response, {@code null} if none.
361         */
362        public JSONObject getPresetUserInfoClaims() {
363                return presetUserInfoClaims;
364        }
365
366
367        /**
368         * Returns the preferred claims transport.
369         *
370         * @return The preferred claims transport, {@code null} if not
371         *         specified (implies UserInfo endpoint).
372         */
373        public ClaimsTransport getClaimsTransport() {
374                return claimsTransport;
375        }
376
377
378        /**
379         * Returns a JSON object representation of this authorisation response.
380         *
381         * @return The JSON object representation.
382         */
383        public JSONObject toJSONObject() {
384
385                JSONObject o = super.toJSONObject();
386
387                o.put("sub", subject.getValue());
388
389                if (authTime != null) {
390                        o.put("auth_time", authTime.getTime() / 1000l);
391                }
392
393                if (acr != null) {
394                        o.put("acr", acr.getValue());
395                }
396
397                if (amrList != null) {
398
399                        List<String> sl = new ArrayList<>(amrList.size());
400
401                        for (AMR amr: amrList) {
402                                sl.add(amr.getValue());
403                        }
404
405                        o.put("amr", sl);
406                }
407
408                o.put("long_lived", longLived);
409
410                o.put("issue_refresh_token", issueRefreshToken);
411
412                o.put("issue_id_token", issueIDToken);
413
414                if (claims != null) {
415                        o.put("claims", new ArrayList<>(claims));
416                }
417
418                if (claimsLocales != null) {
419
420                        o.put("claims_locales", LangTagUtils.toStringList(claimsLocales));
421                }
422
423                if (presetIDTokenClaims != null || presetUserInfoClaims != null) {
424
425                        JSONObject presetClaims = new JSONObject();
426
427                        if (presetIDTokenClaims != null) {
428                                presetClaims.put("id_token", presetIDTokenClaims);
429                        }
430
431                        if (presetUserInfoClaims != null) {
432                                presetClaims.put("userinfo", presetUserInfoClaims);
433                        }
434
435                        o.put("preset_claims", presetClaims);
436                }
437
438                if (claimsTransport != null) {
439                        o.put("claims_transport", claimsTransport);
440                }
441
442                return o;
443        }
444
445
446        /**
447         * Parses an authorisation response from the specified JSON object
448         * representation.
449         *
450         * @param jsonObject The JSON object to parse. Must not be
451         *                   {@code null}.
452         *
453         * @return The authorisation response.
454         */
455        public static PasswordGrantAuthorization parse(final JSONObject jsonObject)
456                throws ParseException {
457
458                GrantAuthorization basicAuthz = GrantAuthorization.parse(jsonObject);
459
460                Subject sub = new Subject(JSONObjectUtils.getString(jsonObject, "sub"));
461
462                Date authTime = null;
463
464                if (jsonObject.containsKey("auth_time")) {
465                        authTime = new Date(JSONObjectUtils.getLong(jsonObject, "auth_time") * 1000l);
466                }
467
468                ACR acr = null;
469
470                if (jsonObject.containsKey("acr")) {
471                        acr = new ACR(JSONObjectUtils.getString(jsonObject, "acr"));
472                }
473
474                List<AMR> amrList = null;
475
476                if (jsonObject.containsKey("amr")) {
477                        String[] sa = JSONObjectUtils.getStringArray(jsonObject, "amr");
478                        amrList = new ArrayList<>(sa.length);
479                        for (String s: sa) {
480                                amrList.add(new AMR(s));
481                        }
482                }
483
484                boolean longLived = false;
485
486                if (jsonObject.containsKey("long_lived")) {
487                        longLived = JSONObjectUtils.getBoolean(jsonObject, "long_lived");
488                }
489
490                boolean issueRefreshToken = false;
491
492                if (jsonObject.containsKey("issue_refresh_token")) {
493                        issueRefreshToken = JSONObjectUtils.getBoolean(jsonObject, "issue_refresh_token");
494                }
495
496                boolean issueIDToken = false;
497
498                if (jsonObject.containsKey("issue_id_token")) {
499                        issueIDToken = JSONObjectUtils.getBoolean(jsonObject, "issue_id_token");
500                }
501
502                Set<String> claims = null;
503
504                if (jsonObject.containsKey("claims")) {
505                        claims = new HashSet<>(Arrays.asList(JSONObjectUtils.getStringArray(jsonObject, "claims")));
506                }
507
508                List<LangTag> claimsLocales = null;
509
510                if (JSONObjectUtils.containsKey(jsonObject, "claims_locales")) {
511
512                        try {
513                                claimsLocales = LangTagUtils.parseLangTagList(JSONObjectUtils.getStringArray(jsonObject, "claims_locales"));
514
515                        } catch (LangTagException e) {
516
517                                throw new ParseException("Invalid claims locales value: " + e.getMessage(), e);
518                        }
519                }
520
521                JSONObject presetIDTokenClaims = null;
522                JSONObject presetUserInfoClaims = null;
523
524                if (jsonObject.containsKey("preset_claims")) {
525
526                        JSONObject presetClaims = JSONObjectUtils.getJSONObject(jsonObject, "preset_claims");
527
528                        if (presetClaims.containsKey("id_token")) {
529                                presetIDTokenClaims = JSONObjectUtils.getJSONObject(presetClaims, "id_token");
530                        }
531
532                        if (presetClaims.containsKey("userinfo")) {
533                                presetUserInfoClaims = JSONObjectUtils.getJSONObject(presetClaims, "userinfo");
534                        }
535                }
536
537                ClaimsTransport claimsTransport = null;
538
539                if (jsonObject.containsKey("claims_transport")) {
540                        String c = JSONObjectUtils.getString(jsonObject, "claims_transport");
541
542                        try {
543                                claimsTransport = ClaimsTransport.valueOf(c.toUpperCase());
544                        } catch (IllegalArgumentException e) {
545                                throw new ParseException("Invalid claims transport");
546                        }
547                }
548
549                return new PasswordGrantAuthorization(
550                        sub, authTime, acr, amrList,
551                        basicAuthz.getScope(),
552                        basicAuthz.getAudience(),
553                        longLived,
554                        basicAuthz.getAccessTokenLifetime(),
555                        basicAuthz.getAccessTokenEncoding(),
556                        issueRefreshToken,
557                        issueIDToken, claims, claimsLocales, presetIDTokenClaims, presetUserInfoClaims, claimsTransport);
558        }
559}