11/*
2- * Copyright (C) 2016 Authlete, Inc.
2+ * Copyright (C) 2016-2018 Authlete, Inc.
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
1818
1919
2020import java .util .Date ;
21-
21+ import java . util . Map ;
2222import javax .ws .rs .core .MultivaluedMap ;
23-
2423import com .authlete .common .dto .Property ;
2524import com .authlete .common .types .User ;
25+ import com .authlete .common .util .Utils ;
2626import com .authlete .jaxrs .spi .AuthorizationDecisionHandlerSpiAdapter ;
2727
2828
@@ -65,6 +65,14 @@ class AuthorizationDecisionHandlerSpiImpl extends AuthorizationDecisionHandlerSp
6565 private String mUserSubject ;
6666
6767
68+ /**
69+ * The value of the "id_token" property in the "claims" request parameter
70+ * (or in the "claims" property in the request object) contained in the
71+ * original authorization request.
72+ */
73+ private Map <String , Object > mIdTokenClaims ;
74+
75+
6876 /**
6977 * Constructor with a request from the form in the authorization page.
7078 *
@@ -73,7 +81,9 @@ class AuthorizationDecisionHandlerSpiImpl extends AuthorizationDecisionHandlerSp
7381 * {@code password} in {@code parameters}.
7482 * </p>
7583 */
76- public AuthorizationDecisionHandlerSpiImpl (MultivaluedMap <String , String > parameters , User user , Date userAuthenticatedAt )
84+ public AuthorizationDecisionHandlerSpiImpl (
85+ MultivaluedMap <String , String > parameters , User user ,
86+ Date userAuthenticatedAt , String idTokenClaims )
7787 {
7888 // If the end-user clicked the "Authorize" button, "authorized"
7989 // is contained in the request.
@@ -106,6 +116,12 @@ public AuthorizationDecisionHandlerSpiImpl(MultivaluedMap<String, String> parame
106116
107117 // The subject (= unique identifier) of the end-user.
108118 mUserSubject = mUser .getSubject ();
119+
120+ // The value of the "id_token" property in the "claims" request parameter
121+ // (or in the "claims" property in the request object) contained in the
122+ // original authorization request. See '5.5. Requesting Claims using the
123+ // "claims" Request Parameter' in OpenID Connect Core 1.0 for details.
124+ mIdTokenClaims = parseJson (idTokenClaims );
109125 }
110126
111127
@@ -137,6 +153,16 @@ public String getUserSubject()
137153 @ Override
138154 public Object getUserClaim (String claimName , String languageTag )
139155 {
156+ // First, check if the claim is a custom one.
157+ Object value = getCustomClaim (claimName , languageTag );
158+
159+ // If the value for the custom claim was obtained.
160+ if (value != null )
161+ {
162+ // Return the value of the custom claim.
163+ return value ;
164+ }
165+
140166 // getUserClaim() is called only when getUserSubject() has returned
141167 // a non-null value. So, mUser is not null when the flow reaches here.
142168 return mUser .getClaim (claimName , languageTag );
@@ -152,4 +178,104 @@ public Property[] getProperties()
152178 // that may be issued as a result of the authorization request.
153179 return null ;
154180 }
181+
182+
183+ @ SuppressWarnings ("unchecked" )
184+ private static Map <String , Object > parseJson (String json )
185+ {
186+ if (json == null )
187+ {
188+ return null ;
189+ }
190+
191+ try
192+ {
193+ return (Map <String , Object >)Utils .fromJson (json , Map .class );
194+ }
195+ catch (Exception e )
196+ {
197+ // Failed to parse the input as JSON.
198+ return null ;
199+ }
200+ }
201+
202+
203+ private Object getCustomClaim (String claimName , String languageTag )
204+ {
205+ // Special behavior for Open Banking Profile.
206+ if ("openbanking_intent_id" .equals (claimName ))
207+ {
208+ // The Open Banking Profile requires that an authorization
209+ // request contains the "openbanking_intent_id" claim and
210+ // the authorization server embeds the value of the claim
211+ // in an ID token.
212+ return getValueFromIdTokenClaims (claimName );
213+ }
214+
215+ return null ;
216+ }
217+
218+
219+ private Object getValueFromIdTokenClaims (String claimName )
220+ {
221+ // Try to extract the entry for the claim from the "id_token"
222+ // property in the "claims" (which was contained in the original
223+ // authorization request).
224+ Map <String , Object > entry = getEntryFromIdTokenClaims (claimName );
225+
226+ // If an entry for the claim is not available.
227+ if (entry == null )
228+ {
229+ // The value of the claim is not available.
230+ return null ;
231+ }
232+
233+ // This method expects that the entry has a "value" property.
234+ return entry .get ("value" );
235+ }
236+
237+
238+ @ SuppressWarnings ("unchecked" )
239+ private Map <String , Object > getEntryFromIdTokenClaims (String claimName )
240+ {
241+ // If the original authorization request does not include
242+ // the "id_token" property in the "claims" request parameter
243+ // (or in the "claims" property in the request object).
244+ if (mIdTokenClaims == null )
245+ {
246+ // No entry for the claim.
247+ return null ;
248+ }
249+
250+ // Extract the entry for the claim from the "id_token" property.
251+ Object entry = mIdTokenClaims .get (claimName );
252+
253+ // If the claim is not included.
254+ if (entry == null )
255+ {
256+ // No entry for the claim.
257+ return null ;
258+ }
259+
260+ // The expected format of a claim in the "id_token" property is
261+ // as follows. See '5.5. Requesting Claims using the "claims"
262+ // Request Parameter' in OpenID Connect Core 1.0 for details.
263+ //
264+ // "claim_name" : {
265+ // "essential": <boolean>, // Optional
266+ // "value": <value>, // Optional
267+ // "values": [<values>] // Optional
268+ // }
269+ //
270+ // Therefore, 'entry' should be able to be parsed as Map.
271+
272+ if (!(entry instanceof Map ))
273+ {
274+ // The format of the claim is invalid.
275+ return null ;
276+ }
277+
278+ // Found the entry for the claim.
279+ return (Map <String , Object >)entry ;
280+ }
155281}
0 commit comments