001/* 002 * oauth2-oidc-sdk 003 * 004 * Copyright 2012-2016, Connect2id Ltd and contributors. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.openid.connect.sdk.token; 019 020 021import java.util.Set; 022 023import com.nimbusds.jwt.JWT; 024import com.nimbusds.jwt.JWTParser; 025import com.nimbusds.oauth2.sdk.ParseException; 026import com.nimbusds.oauth2.sdk.token.AccessToken; 027import com.nimbusds.oauth2.sdk.token.RefreshToken; 028import com.nimbusds.oauth2.sdk.token.Tokens; 029import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 030import net.jcip.annotations.Immutable; 031import net.minidev.json.JSONObject; 032 033 034/** 035 * ID token, access token and optional refresh token. 036 */ 037@Immutable 038public final class OIDCTokens extends Tokens { 039 040 041 /** 042 * The ID Token serialised to a JWT, {@code null} if not specified. 043 */ 044 private final JWT idToken; 045 046 047 /** 048 * The ID Token as raw string (for more efficient serialisation), 049 * {@code null} if not specified. 050 */ 051 private final String idTokenString; 052 053 054 /** 055 * Creates a new OpenID Connect tokens instance. 056 * 057 * @param idToken The ID token. Must not be {@code null}. 058 * @param accessToken The access token. Must not be {@code null}. 059 * @param refreshToken The refresh token. If none {@code null}. 060 */ 061 public OIDCTokens(final JWT idToken, final AccessToken accessToken, final RefreshToken refreshToken) { 062 063 super(accessToken, refreshToken); 064 065 if (idToken == null) { 066 throw new IllegalArgumentException("The ID token must not be null"); 067 } 068 069 this.idToken = idToken; 070 idTokenString = null; 071 } 072 073 074 /** 075 * Creates a new OpenID Connect tokens instance. 076 * 077 * @param idTokenString The ID token string. Must not be {@code null}. 078 * @param accessToken The access token. Must not be {@code null}. 079 * @param refreshToken The refresh token. If none {@code null}. 080 */ 081 public OIDCTokens(final String idTokenString, final AccessToken accessToken, final RefreshToken refreshToken) { 082 083 super(accessToken, refreshToken); 084 085 if (idTokenString == null) { 086 throw new IllegalArgumentException("The ID token string must not be null"); 087 } 088 089 this.idTokenString = idTokenString; 090 idToken = null; 091 } 092 093 094 /** 095 * Creates a new OpenID Connect tokens instance without an ID token. 096 * Intended for token responses from a refresh token grant where the ID 097 * token is optional. 098 * 099 * @param accessToken The access token. Must not be {@code null}. 100 * @param refreshToken The refresh token. If none {@code null}. 101 */ 102 public OIDCTokens(final AccessToken accessToken, final RefreshToken refreshToken) { 103 104 super(accessToken, refreshToken); 105 this.idToken = null; 106 this.idTokenString = null; 107 } 108 109 110 /** 111 * Gets the ID token. 112 * 113 * @return The ID token, {@code null} if none or if parsing to a JWT 114 * failed. 115 */ 116 public JWT getIDToken() { 117 118 if (idToken != null) 119 return idToken; 120 121 if (idTokenString != null) { 122 123 try { 124 return JWTParser.parse(idTokenString); 125 126 } catch (java.text.ParseException e) { 127 128 return null; 129 } 130 } 131 132 return null; 133 } 134 135 136 /** 137 * Gets the ID token string. 138 * 139 * @return The ID token string, {@code null} if none or if 140 * serialisation to a string failed. 141 */ 142 public String getIDTokenString() { 143 144 if (idTokenString != null) 145 return idTokenString; 146 147 if (idToken != null) { 148 149 // Reproduce originally parsed string if any 150 if (idToken.getParsedString() != null) 151 return idToken.getParsedString(); 152 153 try { 154 return idToken.serialize(); 155 156 } catch(IllegalStateException e) { 157 158 return null; 159 } 160 } 161 162 return null; 163 } 164 165 166 @Override 167 public Set<String> getParameterNames() { 168 169 Set<String> paramNames = super.getParameterNames(); 170 if (idToken != null || idTokenString != null) { 171 paramNames.add("id_token"); 172 } 173 return paramNames; 174 } 175 176 177 @Override 178 public JSONObject toJSONObject() { 179 180 JSONObject o = super.toJSONObject(); 181 if (getIDTokenString() != null) { 182 o.put("id_token", getIDTokenString()); 183 } 184 return o; 185 } 186 187 188 /** 189 * Parses an OpenID Connect tokens instance from the specified JSON 190 * object. 191 * 192 * @param jsonObject The JSON object to parse. Must not be {@code null}. 193 * 194 * @return The OpenID Connect tokens. 195 * 196 * @throws ParseException If the JSON object couldn't be parsed to an 197 * OpenID Connect tokens instance. 198 */ 199 public static OIDCTokens parse(final JSONObject jsonObject) 200 throws ParseException { 201 202 AccessToken accessToken = AccessToken.parse(jsonObject); 203 204 RefreshToken refreshToken = RefreshToken.parse(jsonObject); 205 206 if (jsonObject.get("id_token") != null) { 207 208 JWT idToken; 209 try { 210 idToken = JWTParser.parse(JSONObjectUtils.getString(jsonObject, "id_token")); 211 } catch (java.text.ParseException e) { 212 throw new ParseException("Couldn't parse ID token: " + e.getMessage(), e); 213 } 214 215 return new OIDCTokens(idToken, accessToken, refreshToken); 216 217 } else { 218 219 // Likely a token response from a refresh token grant without an ID token 220 return new OIDCTokens(accessToken, refreshToken); 221 } 222 } 223}