001package com.nimbusds.openid.connect.sdk; 002 003 004import net.jcip.annotations.Immutable; 005 006import net.minidev.json.JSONObject; 007 008import com.nimbusds.jwt.JWT; 009import com.nimbusds.jwt.JWTParser; 010 011import com.nimbusds.oauth2.sdk.AccessTokenResponse; 012import com.nimbusds.oauth2.sdk.ParseException; 013import com.nimbusds.oauth2.sdk.SerializeException; 014import com.nimbusds.oauth2.sdk.http.HTTPResponse; 015import com.nimbusds.oauth2.sdk.token.AccessToken; 016import com.nimbusds.oauth2.sdk.token.RefreshToken; 017import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 018 019 020/** 021 * OpenID Connect access token response. 022 * 023 * <p>Example HTTP response: 024 * 025 * <pre> 026 * HTTP/1.1 200 OK 027 * Content-Type: application/json 028 * Cache-Control: no-store 029 * Pragma: no-cache 030 * 031 * { 032 * "access_token" : "SlAV32hkKG", 033 * "token_type" : "Bearer", 034 * "refresh_token" : "8xLOxBtZp8", 035 * "expires_in" : 3600, 036 * "id_token" : "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9zZXJ2Z 037 * XIuZXhhbXBsZS5jb20iLCJ1c2VyX2lkIjoiMjQ4Mjg5NzYxMDAxIiwiYXVkIjoic 038 * zZCaGRSa3F0MyIsIm5vbmNlIjoibi0wUzZfV3pBMk1qIiwiZXhwIjoxMzExMjgxO 039 * TcwLCJpYXQiOjEzMTEyODA5NzB9.RgXxzppVvn1EjUiV3LIZ19SyhdyREe_2jJjW 040 * 5EC8XjNuJfe7Dte8YxRXxssJ67N8MT9mvOI3HOHm4whNx5FCyemyCGyTLHODCeAr 041 * _id029-4JP0KWySoan1jmT7vbGHhu89-l9MTdaEvu7pNZO7DHGwqnMWRe8hdG7jU 042 * ES4w4ReQTygKwXVVOaiGoeUrv6cZdbyOnpGlRlHaiOsv_xMunNVJtn5dLz-0zZwV 043 * ftKVpFuc1pGaVsyZsOtkT32E4c6MDHeCvIDlR5ESC0ct8BLvGJDB5954MjCR4_X2 044 * GAEHonKw4NF8wTmUFvhslYXmjRNFs21Byjn3jNb7lSa3MBfVsw" 045 * } 046 * </pre> 047 * 048 * <p>Related specifications: 049 * 050 * <ul> 051 * <li>OpenID Connect Core 1.0, section 3.1.3.3. 052 * <li>OAuth 2.0 (RFC 6749), sections 4.1.4 and 5.1. 053 * </ul> 054 */ 055@Immutable 056public class OIDCAccessTokenResponse 057 extends AccessTokenResponse { 058 059 060 /** 061 * Optional ID Token serialised to a JWT. 062 */ 063 private final JWT idToken; 064 065 066 /** 067 * Optional ID Token as raw string (for more efficient serialisation). 068 */ 069 private final String idTokenString; 070 071 072 /** 073 * Creates a new OpenID Connect access token response with no ID token. 074 * 075 * @param accessToken The access token. Must not be {@code null}. 076 * @param refreshToken Optional refresh token, {@code null} if none. 077 */ 078 public OIDCAccessTokenResponse(final AccessToken accessToken, 079 final RefreshToken refreshToken) { 080 081 this(accessToken, refreshToken, (String)null); 082 } 083 084 085 /** 086 * Creates a new OpenID Connect access token response. 087 * 088 * @param accessToken The access token. Must not be {@code null}. 089 * @param refreshToken Optional refresh token, {@code null} if none. 090 * @param idToken The ID token. Must be {@code null} if the 091 * request grant type was not 092 * {@link com.nimbusds.oauth2.sdk.GrantType#AUTHORIZATION_CODE}. 093 */ 094 public OIDCAccessTokenResponse(final AccessToken accessToken, 095 final RefreshToken refreshToken, 096 final JWT idToken) { 097 098 super(accessToken, refreshToken); 099 100 this.idToken = idToken; 101 102 idTokenString = null; 103 } 104 105 106 /** 107 * Creates a new OpenID Connect access token response. 108 * 109 * @param accessToken The access token. Must not be {@code null}. 110 * @param refreshToken Optional refresh token, {@code null} if none. 111 * @param idTokenString The ID token string. Must be {@code null} if 112 * the request grant type was not 113 * {@link com.nimbusds.oauth2.sdk.GrantType#AUTHORIZATION_CODE}. 114 */ 115 public OIDCAccessTokenResponse(final AccessToken accessToken, 116 final RefreshToken refreshToken, 117 final String idTokenString) { 118 119 super(accessToken, refreshToken); 120 121 idToken = null; 122 123 this.idTokenString = idTokenString; 124 } 125 126 127 /** 128 * Gets the ID token. 129 * 130 * @return The ID token, {@code null} if none or if parsing to a JWT 131 * failed. 132 */ 133 public JWT getIDToken() { 134 135 if (idToken != null) 136 return idToken; 137 138 if (idTokenString != null) { 139 140 try { 141 return JWTParser.parse(idTokenString); 142 143 } catch (java.text.ParseException e) { 144 145 return null; 146 } 147 } 148 149 return null; 150 } 151 152 153 /** 154 * Gets the ID token string. 155 * 156 * @return The ID token string, {@code null} if none or if 157 * serialisation to a string failed. 158 */ 159 public String getIDTokenString() { 160 161 if (idTokenString != null) 162 return idTokenString; 163 164 if (idToken != null) { 165 166 // Reproduce originally parsed string if any 167 if (idToken.getParsedString() != null) 168 return idToken.getParsedString(); 169 170 try { 171 return idToken.serialize(); 172 173 } catch(IllegalStateException e) { 174 175 return null; 176 } 177 } 178 179 return null; 180 } 181 182 183 /** 184 * Returns the JSON object representing this OpenID Connect access 185 * token response. 186 * 187 * <p>Example JSON object: 188 * 189 * <pre> 190 * { 191 * "access_token" : "SlAV32hkKG", 192 * "token_type" : "Bearer", 193 * "refresh_token": "8xLOxBtZp8", 194 * "expires_in" : 3600, 195 * "id_token" : "eyJ0 ... NiJ9.eyJ1c ... I6IjIifX0.DeWt4Qu ... ZXso" 196 * } 197 * </pre> 198 * 199 * @return The JSON object. 200 * 201 * @throws SerializeException If this OpenID Connect access token 202 * response couldn't be serialised to a JSON 203 * object. 204 */ 205 @Override 206 public JSONObject toJSONObject() 207 throws SerializeException { 208 209 JSONObject o = super.toJSONObject(); 210 211 String idTokenOut = getIDTokenString(); 212 213 if (idTokenOut != null) 214 o.put("id_token", idTokenOut); 215 216 return o; 217 } 218 219 220 /** 221 * Parses an OpenID Connect access token response from the specified 222 * JSON object. 223 * 224 * @param jsonObject The JSON object to parse. Must not be 225 * {@code null}. 226 * 227 * @return The OpenID Connect access token response. 228 * 229 * @throws ParseException If the JSON object couldn't be parsed to an 230 * OpenID Connect access token response. 231 */ 232 public static OIDCAccessTokenResponse parse(final JSONObject jsonObject) 233 throws ParseException { 234 235 AccessTokenResponse atr = AccessTokenResponse.parse(jsonObject); 236 237 JWT idToken = null; 238 239 if (jsonObject.containsKey("id_token")) { 240 241 try { 242 idToken = JWTParser.parse(JSONObjectUtils.getString(jsonObject, "id_token")); 243 244 } catch (java.text.ParseException e) { 245 246 throw new ParseException("Couldn't parse ID token: " + e.getMessage(), e); 247 } 248 } 249 250 251 return new OIDCAccessTokenResponse(atr.getAccessToken(), 252 atr.getRefreshToken(), 253 idToken); 254 } 255 256 257 /** 258 * Parses an OpenID Connect access token response from the specified 259 * HTTP response. 260 * 261 * @param httpResponse The HTTP response. Must not be {@code null}. 262 * 263 * @return The OpenID Connect access token response. 264 * 265 * @throws ParseException If the HTTP response couldn't be parsed to an 266 * OpenID Connect access token response. 267 */ 268 public static OIDCAccessTokenResponse parse(final HTTPResponse httpResponse) 269 throws ParseException { 270 271 httpResponse.ensureStatusCode(HTTPResponse.SC_OK); 272 273 JSONObject jsonObject = httpResponse.getContentAsJSONObject(); 274 275 return parse(jsonObject); 276 } 277}