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}