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.Collections; 022import java.util.HashSet; 023import java.util.Set; 024 025import net.minidev.json.JSONObject; 026 027import com.nimbusds.jwt.JWT; 028import com.nimbusds.jwt.JWTParser; 029import com.nimbusds.oauth2.sdk.ParseException; 030import com.nimbusds.oauth2.sdk.token.AccessToken; 031import com.nimbusds.oauth2.sdk.token.RefreshToken; 032import com.nimbusds.oauth2.sdk.token.Tokens; 033import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 034 035 036/** 037 * ID token, access token and optional refresh token. 038 */ 039public final class OIDCTokens extends Tokens { 040 041 042 /** 043 * The ID Token serialised to a JWT, {@code null} if not specified. 044 */ 045 private final JWT idToken; 046 047 048 /** 049 * The ID Token as raw string (for more efficient serialisation), 050 * {@code null} if not specified. 051 */ 052 private final String idTokenString; 053 054 055 /** 056 * Creates a new OpenID Connect tokens instance. 057 * 058 * @param idToken The ID token. Must not be {@code null}. 059 * @param accessToken The access token. Must not be {@code null}. 060 * @param refreshToken The refresh token. If none {@code null}. 061 */ 062 public OIDCTokens(final JWT idToken, final AccessToken accessToken, final RefreshToken refreshToken) { 063 064 super(accessToken, refreshToken); 065 066 if (idToken == null) { 067 throw new IllegalArgumentException("The ID token must not be null"); 068 } 069 070 this.idToken = idToken; 071 idTokenString = null; 072 } 073 074 075 /** 076 * Creates a new OpenID Connect tokens instance. 077 * 078 * @param idTokenString The ID token string. Must not be {@code null}. 079 * @param accessToken The access token. Must not be {@code null}. 080 * @param refreshToken The refresh token. If none {@code null}. 081 */ 082 public OIDCTokens(final String idTokenString, final AccessToken accessToken, final RefreshToken refreshToken) { 083 084 super(accessToken, refreshToken); 085 086 if (idTokenString == null) { 087 throw new IllegalArgumentException("The ID token string must not be null"); 088 } 089 090 this.idTokenString = idTokenString; 091 idToken = null; 092 } 093 094 095 /** 096 * Creates a new OpenID Connect tokens instance without an ID token. 097 * Intended for token responses from a refresh token grant where the ID 098 * token is optional. 099 * 100 * @param accessToken The access token. Must not be {@code null}. 101 * @param refreshToken The refresh token. If none {@code null}. 102 */ 103 public OIDCTokens(final AccessToken accessToken, final RefreshToken refreshToken) { 104 105 super(accessToken, refreshToken); 106 this.idToken = null; 107 this.idTokenString = null; 108 } 109 110 111 /** 112 * Gets the ID token. 113 * 114 * @return The ID token, {@code null} if none or if parsing to a JWT 115 * failed. 116 */ 117 public JWT getIDToken() { 118 119 if (idToken != null) 120 return idToken; 121 122 if (idTokenString != null) { 123 124 try { 125 return JWTParser.parse(idTokenString); 126 127 } catch (java.text.ParseException e) { 128 129 return null; 130 } 131 } 132 133 return null; 134 } 135 136 137 /** 138 * Gets the ID token string. 139 * 140 * @return The ID token string, {@code null} if none or if 141 * serialisation to a string failed. 142 */ 143 public String getIDTokenString() { 144 145 if (idTokenString != null) 146 return idTokenString; 147 148 if (idToken != null) { 149 150 // Reproduce originally parsed string if any 151 if (idToken.getParsedString() != null) 152 return idToken.getParsedString(); 153 154 try { 155 return idToken.serialize(); 156 157 } catch(IllegalStateException e) { 158 159 return null; 160 } 161 } 162 163 return null; 164 } 165 166 167 @Override 168 public Set<String> getParameterNames() { 169 170 Set<String> paramNames = new HashSet<>(super.getParameterNames()); 171 if (idToken != null || idTokenString != null) { 172 paramNames.add("id_token"); 173 } 174 return Collections.unmodifiableSet(paramNames); 175 } 176 177 178 @Override 179 public JSONObject toJSONObject() { 180 181 JSONObject o = super.toJSONObject(); 182 if (getIDTokenString() != null) { 183 o.put("id_token", getIDTokenString()); 184 } 185 return o; 186 } 187 188 189 /** 190 * Parses an OpenID Connect tokens instance from the specified JSON 191 * object. 192 * 193 * @param jsonObject The JSON object to parse. Must not be {@code null}. 194 * 195 * @return The OpenID Connect tokens. 196 * 197 * @throws ParseException If the JSON object couldn't be parsed to an 198 * OpenID Connect tokens instance. 199 */ 200 public static OIDCTokens parse(final JSONObject jsonObject) 201 throws ParseException { 202 203 AccessToken accessToken = AccessToken.parse(jsonObject); 204 205 RefreshToken refreshToken = RefreshToken.parse(jsonObject); 206 207 if (jsonObject.get("id_token") != null) { 208 209 JWT idToken; 210 try { 211 idToken = JWTParser.parse(JSONObjectUtils.getString(jsonObject, "id_token")); 212 } catch (java.text.ParseException e) { 213 throw new ParseException("Couldn't parse ID token: " + e.getMessage(), e); 214 } 215 216 return new OIDCTokens(idToken, accessToken, refreshToken); 217 218 } else { 219 220 // Likely a token response from a refresh token grant without an ID token 221 return new OIDCTokens(accessToken, refreshToken); 222 } 223 } 224}