001package com.nimbusds.openid.connect.provider.spi.grants;
002
003
004import java.util.*;
005
006import net.minidev.json.JSONObject;
007
008import com.nimbusds.oauth2.sdk.ParseException;
009import com.nimbusds.oauth2.sdk.Scope;
010import com.nimbusds.oauth2.sdk.id.Audience;
011import com.nimbusds.oauth2.sdk.id.Subject;
012import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
013
014import com.nimbusds.openid.connect.sdk.claims.ACR;
015import com.nimbusds.openid.connect.sdk.claims.AMR;
016
017
018/**
019 * Authorisation response from a {@link PasswordGrantHandler}.
020 *
021 * <p>The minimum details it contains is the identifier of the authenticated
022 * subject (end-user) and the authorised scope values. The other parameters are
023 * optional or have suitable defaults.
024 */
025public class PasswordGrantAuthorization extends GrantAuthorization {
026
027
028        /**
029         * The identifier of the authorised subject.
030         */
031        private final Subject subject;
032
033
034        /**
035         * The time of the subject authentication. If {@code null} it will be
036         * set to now. Applies only if an ID token is issued.
037         */
038        private final Date authTime;
039
040
041        /**
042         * The Authentication Context Class Reference (ACR), {@code null} if
043         * not specified. Applies only if an ID token is issued.
044         */
045        private final ACR acr;
046
047
048        /**
049         * The Authentication Methods Reference (AMR) list, {@code null} if not
050         * specified. Applies only if an ID token is issued.
051         */
052        private final List<AMR> amrList;
053
054
055        /**
056         * Controls the authorisation lifetime. {@code true} for a long-lived
057         * authorisation (implies persistence), {@code false} for a short-lived
058         * one.
059         */
060        private final boolean longLived;
061
062
063        /**
064         * The refresh token specification.
065         */
066        private final RefreshTokenSpec refreshTokenSpec;
067
068
069        /**
070         * The ID token specification.
071         */
072        private final IDTokenSpec idTokenSpec;
073
074
075        /**
076         * The OpenID Connect claims spec.
077         */
078        private final ClaimsSpec claimsSpec;
079
080
081        /**
082         * Creates a new OAuth 2.0 - only authorisation response from a
083         * {@link PasswordGrantHandler}.
084         *
085         * @param subject             The identifier of the authorised
086         *                            subject. Must not be {@code null}.
087         * @param scope               The authorised scope values. Must not be
088         *                            {@code null}.
089         * @param audList             Explicit list of audiences for the access
090         *                            token, {@code null} if not specified.
091         * @param longLived           Controls the authorisation lifetime.
092         *                            {@code true} for a long-lived
093         *                            authorisation (implies persistence),
094         *                            {@code false} for a short-lived
095         *                            (transient) one.
096         * @param accessTokenSpec     The access token specification. Must not
097         *                            be {@code null}.
098         * @param refreshTokenSpec    The refresh token specification. Must not
099         *                            be {@code null}.
100         * @param data                Optional authorisation data as a JSON
101         *                            object, {@code null} if not specified.
102         */
103        public PasswordGrantAuthorization(final Subject subject,
104                                          final Scope scope,
105                                          final List<Audience> audList,
106                                          final boolean longLived,
107                                          final AccessTokenSpec accessTokenSpec,
108                                          final RefreshTokenSpec refreshTokenSpec,
109                                          final JSONObject data) {
110
111                this (subject, null, null, null, scope, audList, longLived, accessTokenSpec, refreshTokenSpec, new IDTokenSpec(), new ClaimsSpec(), data);
112        }
113
114
115        /**
116         * Creates a new OpenID Connect / OAuth 2.0 authorisation response from
117         * a {@link PasswordGrantHandler}.
118         *
119         * @param subject              The identifier of the authorised
120         *                             subject. Must not be {@code null}.
121         * @param authTime             The time of the subject authentication.
122         *                             If {@code null} it will be set to now.
123         *                             Applies only if an ID token is issued.
124         * @param acr                  The Authentication Context Class
125         *                             Reference (ACR), {@code null} if not
126         *                             specified. Applies only if an ID token
127         *                             is issued.
128         * @param amrList              The Authentication Methods Reference
129         *                             (AMR) list, {@code null} if not
130         *                             specified. Applies only if an ID token
131         *                             is issued.
132         * @param scope                The authorised scope values. Must not be
133         *                             {@code null}.
134         * @param audList              Explicit list of audiences for the
135         *                             access token, {@code null} if not
136         *                             specified.
137         * @param longLived            Controls the authorisation lifetime.
138         *                             {@code true} for a long-lived
139         *                             authorisation (implies persistence),
140         *                             {@code false} for a short-lived one.
141         * @param accessTokenSpec      The access token specification. Must not
142         *                             be {@code null}.
143         * @param refreshTokenSpec     The refresh token specification. Must not
144         *                             be {@code null}.
145         * @param idTokenSpec          The ID token specification. Must not be
146         *                             {@code null}.
147         * @param claimsSpec           The claims specification.
148         * @param data                 Optional authorisation data as a JSON
149         *                             object, {@code null} if not specified.
150         */
151        public PasswordGrantAuthorization(final Subject subject,
152                                          final Date authTime,
153                                          final ACR acr,
154                                          final List<AMR> amrList,
155                                          final Scope scope,
156                                          final List<Audience> audList,
157                                          final boolean longLived,
158                                          final AccessTokenSpec accessTokenSpec,
159                                          final RefreshTokenSpec refreshTokenSpec,
160                                          final IDTokenSpec idTokenSpec,
161                                          final ClaimsSpec claimsSpec,
162                                          final JSONObject data) {
163
164                super(scope, audList, accessTokenSpec, data);
165
166                if (subject == null) {
167                        throw new IllegalArgumentException("The subject must not be null");
168                }
169
170                this.subject = subject;
171
172                this.authTime = authTime;
173                this.acr = acr;
174                this.amrList = amrList;
175                this.longLived = longLived;
176
177                if (refreshTokenSpec == null) {
178                        throw new IllegalArgumentException("The refresh token specification must not be null");
179                }
180
181                if (longLived) {
182                        this.refreshTokenSpec = refreshTokenSpec;
183                } else {
184                        // Defaults to no-issue
185                        this.refreshTokenSpec = new RefreshTokenSpec();
186                }
187
188                if (idTokenSpec == null) {
189                        throw new IllegalArgumentException("The ID token specification must not be null");
190                }
191
192                this.idTokenSpec = idTokenSpec;
193
194                if (claimsSpec == null) {
195                        throw new IllegalArgumentException("The claim specification must not be null");
196                }
197
198                this.claimsSpec = claimsSpec;
199        }
200
201
202        /**
203         * Returns the authorised subject.
204         *
205         * @return The identifier of the authorised subject.
206         */
207        public Subject getSubject() {
208
209                return subject;
210        }
211
212
213        /**
214         * Returns the time of the subject authentication.
215         *
216         * @return The time of the subject authentication. If {@code null} it
217         *         will be set to now. Applies only if an ID token is issued.
218         */
219        public Date getAuthTime() {
220
221                return authTime;
222        }
223
224
225        /**
226         * Returns the Authentication Context Class Reference (ACR).
227         *
228         * @return The Authentication Context Class Reference (ACR),
229         *         {@code null} if not specified. Applies only if an ID token
230         *         is issued.
231         */
232        public ACR getACR() {
233
234                return acr;
235        }
236
237
238        /**
239         * Returns The Authentication Methods Reference (AMR) list.
240         *
241         * @return The Authentication Methods Reference (AMR) list,
242         *         {@code null} if not specified. Applies only if an ID token
243         *         is issued.
244         */
245        public List<AMR> getAMRList() {
246
247                return amrList;
248        }
249
250
251        /**
252         * Returns the authorisation lifetime.
253         *
254         * @return {@code true} for a long-lived authorisation (implies
255         *         persistence), {@code false} for a short-lived one.
256         */
257        public boolean isLongLived() {
258
259                return longLived;
260        }
261
262
263        /**
264         * Returns the refresh token specification.
265         *
266         * @return The refresh token specification.
267         */
268        public RefreshTokenSpec getRefreshTokenSpec() {
269
270                return refreshTokenSpec;
271        }
272
273
274        /**
275         * Returns the ID token specification.
276         *
277         * @return The ID token specification.
278         */
279        public IDTokenSpec getIDTokenSpec() {
280
281                return idTokenSpec;
282        }
283
284
285        /**
286         * Returns the claims specification.
287         *
288         * @return The claims specification.
289         */
290        public ClaimsSpec getClaimsSpec() {
291
292                return claimsSpec;
293        }
294
295
296        /**
297         * Returns a JSON object representation of this authorisation response.
298         *
299         * @return The JSON object representation.
300         */
301        public JSONObject toJSONObject() {
302
303                JSONObject o = super.toJSONObject();
304
305                o.put("sub", subject.getValue());
306
307                if (authTime != null) {
308                        o.put("auth_time", authTime.getTime() / 1000l);
309                }
310
311                if (acr != null) {
312                        o.put("acr", acr.getValue());
313                }
314
315                if (amrList != null) {
316
317                        List<String> sl = new ArrayList<>(amrList.size());
318
319                        for (AMR amr: amrList) {
320                                sl.add(amr.getValue());
321                        }
322
323                        o.put("amr", sl);
324                }
325
326                o.put("long_lived", longLived);
327
328                if (longLived && refreshTokenSpec.issue()) {
329                        o.put("refresh_token", refreshTokenSpec.toJSONObject());
330                }
331
332                if (idTokenSpec.issue()) {
333                        o.put("id_token", idTokenSpec.toJSONObject());
334                }
335
336                o.putAll(claimsSpec.toJSONObject());
337
338                return o;
339        }
340
341
342        /**
343         * Parses an authorisation response from the specified JSON object
344         * representation.
345         *
346         * @param o The JSON object to parse. Must not be {@code null}.
347         *
348         * @return The authorisation response.
349         *
350         * @throws ParseException If parsing failed.
351         */
352        public static PasswordGrantAuthorization parse(final JSONObject o)
353                throws ParseException {
354
355                GrantAuthorization basicAuthz = GrantAuthorization.parse(o);
356
357                Subject sub = new Subject(JSONObjectUtils.getString(o, "sub"));
358
359                Date authTime = null;
360
361                if (o.containsKey("auth_time")) {
362                        authTime = new Date(JSONObjectUtils.getLong(o, "auth_time") * 1000l);
363                }
364
365                ACR acr = null;
366
367                if (o.containsKey("acr")) {
368                        acr = new ACR(JSONObjectUtils.getString(o, "acr"));
369                }
370
371                List<AMR> amrList = null;
372
373                if (o.containsKey("amr")) {
374                        String[] sa = JSONObjectUtils.getStringArray(o, "amr");
375                        amrList = new ArrayList<>(sa.length);
376                        for (String s: sa) {
377                                amrList.add(new AMR(s));
378                        }
379                }
380
381                boolean longLived = false;
382
383                if (o.containsKey("long_lived")) {
384                        longLived = JSONObjectUtils.getBoolean(o, "long_lived");
385                }
386
387                RefreshTokenSpec rtSpec = new RefreshTokenSpec();
388
389                if (longLived && o.containsKey("refresh_token")) {
390
391                        rtSpec = RefreshTokenSpec.parse(JSONObjectUtils.getJSONObject(o, "refresh_token"));
392                }
393
394                IDTokenSpec idSpec = new IDTokenSpec();
395
396                if (o.containsKey("id_token")) {
397
398                        idSpec = IDTokenSpec.parse(JSONObjectUtils.getJSONObject(o, "id_token"));
399                }
400
401                ClaimsSpec clSpec = ClaimsSpec.parse(o);
402
403                return new PasswordGrantAuthorization(
404                        sub, authTime, acr, amrList,
405                        basicAuthz.getScope(),
406                        basicAuthz.getAudience(),
407                        longLived,
408                        basicAuthz.getAccessTokenSpec(),
409                        rtSpec,
410                        idSpec,
411                        clSpec,
412                        basicAuthz.getData());
413        }
414}