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.oauth2.sdk.token;
019
020
021import java.util.List;
022import java.util.Map;
023
024import net.jcip.annotations.Immutable;
025import net.minidev.json.JSONObject;
026
027import com.nimbusds.oauth2.sdk.ParseException;
028import com.nimbusds.oauth2.sdk.Scope;
029import com.nimbusds.oauth2.sdk.http.HTTPRequest;
030
031
032/**
033 * DPoP access token.
034 *
035 * <p>Example DPoP access token serialised to JSON:
036 *
037 * <pre>
038 * {
039 *   "access_token" : "aeniniu3oogh2quoot7Aipie9IeGh3te",
040 *   "token_type"   : "DPoP",
041 *   "expires_in"   : 3600,
042 *   "scope"        : "read write"
043 * }
044 * </pre>
045 *
046 * <p>The above example token serialised to a HTTP Authorization header:
047 *
048 * <pre>
049 * Authorization: DPoP aeniniu3oogh2quoot7Aipie9IeGh3te
050 * </pre>
051 *
052 * <p>Related specifications:
053 *
054 * <ul>
055 *     <li>OAuth 2.0 (RFC 6749), sections 1.4 and 5.1.
056 *     <li>OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer
057 *         (DPoP) (draft-ietf-oauth-dpop-03)
058 *     <li>OAuth 2.0 Token Exchange (RFC 8693), section 3.
059 * </ul>
060 */
061@Immutable
062public class DPoPAccessToken extends AccessToken {
063        
064        
065        private static final long serialVersionUID = 7745184045632691024L;
066        
067        
068        /**
069         * Creates a new minimal DPoP access token with the specified value.
070         * The optional lifetime, scope and token type URI are left
071         * unspecified.
072         *
073         * @param value The access token value. Must not be {@code null} or
074         *              empty string.
075         */
076        public DPoPAccessToken(final String value) {
077        
078                this(value, 0L, null);
079        }
080        
081        
082        /**
083         * Creates a new DPoP access token with the specified value. The
084         * optional token type URI is left unspecified.
085         *
086         * @param value    The access token value. Must not be {@code null} or
087         *                 empty string.
088         * @param lifetime The lifetime in seconds, 0 if not specified.
089         * @param scope    The scope, {@code null} if not specified.
090         */
091        public DPoPAccessToken(final String value, final long lifetime, final Scope scope) {
092        
093                this(value, lifetime, scope, null);
094        }
095
096        /**
097         * Creates a new DPoP access token with the specified value.
098         *
099         * @param value           The access token value. Must not be
100         *                        {@code null} or empty string.
101         * @param lifetime        The lifetime in seconds, 0 if not specified.
102         * @param scope           The scope, {@code null} if not specified.
103         * @param issuedTokenType The token type URI, {@code null} if not
104         *                        specified.
105         */
106        public DPoPAccessToken(final String value, final long lifetime, final Scope scope, final TokenTypeURI issuedTokenType) {
107
108                super(AccessTokenType.DPOP, value, lifetime, scope, issuedTokenType);
109        }
110        
111        
112        /**
113         * Returns the HTTP Authorization header value for this DPoP access
114         * token.
115         *
116         * <p>Example:
117         *
118         * <pre>
119         * Authorization: DPoP aeniniu3oogh2quoot7Aipie9IeGh3te
120         * </pre>
121         *
122         * @return The HTTP Authorization header.
123         */
124        @Override
125        public String toAuthorizationHeader(){
126        
127                return "DPoP " + getValue();
128        }
129        
130        
131        @Override
132        public boolean equals(final Object object) {
133        
134                return object instanceof DPoPAccessToken &&
135                       this.toString().equals(object.toString());
136        }
137
138
139        /**
140         * Parses a DPoP access token from a JSON object access token
141         * response.
142         *
143         * @param jsonObject The JSON object to parse. Must not be 
144         *                   {@code null}.
145         *
146         * @return The DPoP access token.
147         *
148         * @throws ParseException If the JSON object couldn't be parsed to a
149         *                        DPoP access token.
150         */
151        public static DPoPAccessToken parse(final JSONObject jsonObject)
152                throws ParseException {
153
154                AccessTokenUtils.parseAndEnsureType(jsonObject, AccessTokenType.DPOP);
155                String accessTokenValue = AccessTokenUtils.parseValue(jsonObject);
156                long lifetime = AccessTokenUtils.parseLifetime(jsonObject);
157                Scope scope = AccessTokenUtils.parseScope(jsonObject);
158                TokenTypeURI issuedTokenType = AccessTokenUtils.parseIssuedTokenType(jsonObject);
159                return new DPoPAccessToken(accessTokenValue, lifetime, scope, issuedTokenType);
160        }
161        
162        
163        /**
164         * Parses an HTTP Authorization header for a DPoP access token.
165         *
166         * @param header The HTTP Authorization header value to parse. May be
167         *               {@code null} if the header is missing, in which case
168         *               an exception will be thrown.
169         *
170         * @return The DPoP access token.
171         *
172         * @throws ParseException If the HTTP Authorization header value 
173         *                        couldn't be parsed to a DPoP access token.
174         */
175        public static DPoPAccessToken parse(final String header)
176                throws ParseException {
177                
178                return new DPoPAccessToken(AccessTokenUtils.parseValueFromHeader(header, AccessTokenType.DPOP));
179        }
180        
181        
182        /**
183         * Parses a query or form parameters map for a bearer access token.
184         *
185         * @param parameters The query parameters. Must not be {@code null}.
186         *
187         * @return The bearer access token.
188         *
189         * @throws ParseException If a bearer access token wasn't found in the
190         *                        parameters.
191         */
192        public static DPoPAccessToken parse(final Map<String,List<String>> parameters)
193                throws ParseException {
194                
195                return new DPoPAccessToken(AccessTokenUtils.parseValueFromQueryParameters(parameters, AccessTokenType.DPOP));
196        }
197        
198        
199        
200        /**
201         * Parses an HTTP request for a bearer access token.
202         * 
203         * @param request The HTTP request to parse. Must not be {@code null}.
204         * 
205         * @return The bearer access token.
206         * 
207         * @throws ParseException If a bearer access token wasn't found in the
208         *                        HTTP request.
209         */
210        public static DPoPAccessToken parse(final HTTPRequest request)
211                throws ParseException {
212
213                // See http://tools.ietf.org/html/rfc6750#section-2
214                String authzHeader = request.getAuthorization();
215
216                if (authzHeader != null) {
217                        return parse(authzHeader);
218                }
219
220                // Try alternative token locations, form and query string are
221                // parameters are not differentiated here
222                Map<String,List<String>> params = request.getQueryParameters();
223                return parse(params);
224        }
225}