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}