1717package org .oidc .msg .oidc ;
1818
1919import java .util .HashMap ;
20+ import java .util .List ;
2021import java .util .Map ;
2122
2223import org .oidc .msg .InvalidClaimException ;
2526/** ID Token as in http://openid.net/specs/openid-connect-core-1_0.html#IDToken. */
2627public class IDToken extends OpenIDSchema {
2728
29+ /**
30+ * TODO functionality: Missing to_jwt related functionality like adding c_hash, jti etc. These are
31+ * OP features.
32+ */
33+
34+ /** Issuer to match the id token to. */
35+ private String issuer ;
36+
37+ /** Client ID to match the id token to. */
38+ private String clientId ;
39+
40+ /** Nonce to match the id token to. */
41+ private String nonce ;
42+
43+ /** Skew in seconds for calculating if the id token has expired or not. */
44+ private long skew = 0 ;
45+
46+ /** Nonce storage time in seconds. */
47+ private long storageTime = 4 * 3600 ;
48+
2849 {
2950 // Updating parameter requirements.
3051 paramVerDefs .put ("iss" , ParameterVerification .SINGLE_REQUIRED_STRING .getValue ());
@@ -61,6 +82,57 @@ public IDToken(Map<String, Object> claims) {
6182 super (claims );
6283 }
6384
85+ /**
86+ * Set Issuer to use when verifying id token.
87+ *
88+ * @param issuer
89+ * Issuer to match the id token to.
90+ */
91+ public void setIssuer (String issuer ) {
92+ this .issuer = issuer ;
93+ }
94+
95+ /**
96+ * Set Client ID to use when verifying id token.
97+ *
98+ * @param clientId
99+ * Client ID to match the id token to.
100+ */
101+ public void setClientId (String clientId ) {
102+ this .clientId = clientId ;
103+ }
104+
105+ /**
106+ * Set Nonce to use when verifying id token. Comparison is done only if id token has nonce.
107+ *
108+ * @param nonce
109+ * Nonce to match the id token to.
110+ */
111+ public void setNonce (String nonce ) {
112+ this .nonce = nonce ;
113+ }
114+
115+ /**
116+ * Set Skew in seconds for calculating if the id token has expired or not.
117+ *
118+ * @param skew
119+ * Skew in seconds for calculating if the id token has expired or not.
120+ */
121+ public void setSkew (long skew ) {
122+ this .skew = skew ;
123+ }
124+
125+ /**
126+ * Set nonce storage time in seconds. Id token must not have been issued longer ago than nonce
127+ * storage time is. Default is 4h.
128+ *
129+ * @param storageTime
130+ * nonce storage time in seconds
131+ */
132+ public void setStorageTime (long storageTime ) {
133+ this .storageTime = storageTime ;
134+ }
135+
64136 /**
65137 * Verifies the presence of required message parameters. Verifies the the format of message
66138 * parameters.
@@ -69,17 +141,55 @@ public IDToken(Map<String, Object> claims) {
69141 * @throws InvalidClaimException
70142 * if verification fails.
71143 */
144+ @ SuppressWarnings ("unchecked" )
72145 public boolean verify () throws InvalidClaimException {
73146 super .verify ();
74- // TODO:Check issuer. Requires setter for issuer to compare against.
75- // TODO:Check client_id is among aud. Requires a setter for client_id to compare against.
76- // TODO:if multiple aud, check azp is in audience.
77- // TODO:if client_id is set and azp exists, they must match.
78- // TODO:check exp is not in the past. Requires setter for skew to allow skew.
79- // TODO:check iat+NONCE_STORAGE_TIME < now - skew. Requires setter but leave it until
80- // requirement is clear. NONCE_STORAGE_TIME = 4 * 3600
81- // TODO: Check nonce. Requires setter for nonce to compare against.
82- return true ;
147+
148+ if (issuer != null && !issuer .equals (getClaims ().get ("iss" ))) {
149+ getError ().getMessages ()
150+ .add (String .format (
151+ "Issuer mismatch, expected value '%s' for iss claim but got '%s' instead" , issuer ,
152+ getClaims ().get ("iss" )));
153+ }
154+
155+ if (clientId != null && !((List <String >) getClaims ().get ("aud" )).contains (clientId )) {
156+ getError ().getMessages ()
157+ .add (String .format ("Client ID '%s' is not listed in the aud claim" , clientId ));
158+ }
159+
160+ if (((List <String >) getClaims ().get ("aud" )).size () > 1 && (getClaims ().get ("azp" ) == null
161+ || !((List <String >) getClaims ().get ("aud" )).contains (getClaims ().get ("azp" )))) {
162+ getError ().getMessages ()
163+ .add ("If claim aud has multiple values one of them must have value of azp claim." );
164+ }
165+
166+ if (getClaims ().get ("azp" ) != null && clientId != null
167+ && !clientId .equals ((String ) getClaims ().get ("azp" ))) {
168+ getError ().getMessages ().add (String .format (
169+ "Client ID '%s' should equal to azp claim value '%s'" , clientId , getClaims ().get ("azp" )));
170+ }
171+
172+ long now = System .currentTimeMillis () / 1000 ;
173+ if (now - skew > (long ) getClaims ().get ("exp" )) {
174+ getError ().getMessages ().add ("Claim exp is in the past" );
175+ }
176+
177+ if ((long ) getClaims ().get ("iat" ) + storageTime < now - skew ) {
178+ getError ().getMessages ().add ("id token has been issued too long ago" );
179+ }
180+
181+ if (nonce != null && getClaims ().get ("nonce" ) != null
182+ && !nonce .equals (getClaims ().get ("nonce" ))) {
183+ getError ().getMessages ().add ("nonce mismatch" );
184+ }
185+
186+ if (getError ().getMessages ().size () > 0 ) {
187+ this .setVerified (false );
188+ throw new InvalidClaimException (
189+ "Message parameter verification failed. See Error object for details" );
190+ }
191+
192+ return hasError ();
83193
84194 }
85195}
0 commit comments