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 com.nimbusds.oauth2.sdk.ParseException;
022import com.nimbusds.oauth2.sdk.Scope;
023import com.nimbusds.oauth2.sdk.http.HTTPRequest;
024import com.nimbusds.oauth2.sdk.rar.AuthorizationDetail;
025import net.jcip.annotations.Immutable;
026import net.minidev.json.JSONObject;
027
028import java.util.List;
029import java.util.Map;
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 an 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) (RFC 9449)
058 *     <li>OAuth 2.0 Rich Authorization Requests (RFC 9396), section 7.
059 *     <li>OAuth 2.0 Token Exchange (RFC 8693), section 3.
060 * </ul>
061 */
062@Immutable
063public class DPoPAccessToken extends AccessToken {
064        
065        
066        private static final long serialVersionUID = 7745184045632691024L;
067        
068        
069        /**
070         * Creates a new minimal DPoP access token with the specified value.
071         * The optional lifetime, scope and token type URI are left
072         * unspecified.
073         *
074         * @param value The access token value. Must not be {@code null} or
075         *              empty string.
076         */
077        public DPoPAccessToken(final String value) {
078        
079                this(value, 0L, null);
080        }
081        
082        
083        /**
084         * Creates a new DPoP access token with the specified value. The
085         * optional token type URI is left unspecified.
086         *
087         * @param value    The access token value. Must not be {@code null} or
088         *                 empty string.
089         * @param lifetime The lifetime in seconds, 0 if not specified.
090         * @param scope    The scope, {@code null} if not specified.
091         */
092        public DPoPAccessToken(final String value, final long lifetime, final Scope scope) {
093        
094                this(value, lifetime, scope, null);
095        }
096
097
098        /**
099         * Creates a new DPoP access token with the specified value.
100         *
101         * @param value           The access token value. Must not be
102         *                        {@code null} or empty string.
103         * @param lifetime        The lifetime in seconds, 0 if not specified.
104         * @param scope           The scope, {@code null} if not specified.
105         * @param issuedTokenType The token type URI, {@code null} if not
106         *                        specified.
107         */
108        public DPoPAccessToken(final String value,
109                               final long lifetime,
110                               final Scope scope,
111                               final TokenTypeURI issuedTokenType) {
112
113                super(AccessTokenType.DPOP, value, lifetime, scope, issuedTokenType);
114        }
115
116
117        /**
118         * Creates a new DPoP access token with the specified value.
119         *
120         * @param value                The access token value. Must not be
121         *                             {@code null} or empty string.
122         * @param lifetime             The lifetime in seconds, 0 if not
123         *                             specified.
124         * @param scope                The scope, {@code null} if not specified.
125         * @param authorizationDetails The authorisation details, {@code null}
126         *                             if not specified.
127         * @param issuedTokenType      The token type URI, {@code null} if not
128         *                             specified.
129         */
130        public DPoPAccessToken(final String value,
131                               final long lifetime,
132                               final Scope scope,
133                               final List<AuthorizationDetail> authorizationDetails,
134                               final TokenTypeURI issuedTokenType) {
135
136                super(AccessTokenType.DPOP, value, lifetime, scope, authorizationDetails, issuedTokenType);
137        }
138        
139        
140        /**
141         * Returns the HTTP Authorization header value for this DPoP access
142         * token.
143         *
144         * <p>Example:
145         *
146         * <pre>
147         * Authorization: DPoP aeniniu3oogh2quoot7Aipie9IeGh3te
148         * </pre>
149         *
150         * @return The HTTP Authorization header.
151         */
152        @Override
153        public String toAuthorizationHeader(){
154        
155                return "DPoP " + getValue();
156        }
157        
158        
159        @Override
160        public boolean equals(final Object object) {
161        
162                return object instanceof DPoPAccessToken &&
163                       this.toString().equals(object.toString());
164        }
165
166
167        /**
168         * Parses a DPoP access token from a JSON object access token
169         * response.
170         *
171         * @param jsonObject The JSON object to parse. Must not be 
172         *                   {@code null}.
173         *
174         * @return The DPoP access token.
175         *
176         * @throws ParseException If the JSON object couldn't be parsed to a
177         *                        DPoP access token.
178         */
179        public static DPoPAccessToken parse(final JSONObject jsonObject)
180                throws ParseException {
181
182                AccessTokenUtils.parseAndEnsureType(jsonObject, AccessTokenType.DPOP);
183                String accessTokenValue = AccessTokenUtils.parseValue(jsonObject);
184                long lifetime = AccessTokenUtils.parseLifetime(jsonObject);
185                Scope scope = AccessTokenUtils.parseScope(jsonObject);
186                List<AuthorizationDetail> authorizationDetails = AccessTokenUtils.parseAuthorizationDetails(jsonObject);
187                TokenTypeURI issuedTokenType = AccessTokenUtils.parseIssuedTokenType(jsonObject);
188                return new DPoPAccessToken(accessTokenValue, lifetime, scope, authorizationDetails, issuedTokenType);
189        }
190        
191        
192        /**
193         * Parses an HTTP Authorization header for a DPoP access token.
194         *
195         * @param header The HTTP Authorization header value to parse. May be
196         *               {@code null} if the header is missing, in which case
197         *               an exception will be thrown.
198         *
199         * @return The DPoP access token.
200         *
201         * @throws ParseException If the HTTP Authorization header value 
202         *                        couldn't be parsed to a DPoP access token.
203         */
204        public static DPoPAccessToken parse(final String header)
205                throws ParseException {
206                
207                return new DPoPAccessToken(AccessTokenUtils.parseValueFromHeader(header, AccessTokenType.DPOP));
208        }
209        
210        
211        /**
212         * Parses a query or form parameters map for a bearer access token.
213         *
214         * @param parameters The query parameters. Must not be {@code null}.
215         *
216         * @return The bearer access token.
217         *
218         * @throws ParseException If a bearer access token wasn't found in the
219         *                        parameters.
220         */
221        public static DPoPAccessToken parse(final Map<String,List<String>> parameters)
222                throws ParseException {
223                
224                return new DPoPAccessToken(AccessTokenUtils.parseValueFromQueryParameters(parameters, AccessTokenType.DPOP));
225        }
226        
227        
228        
229        /**
230         * Parses an HTTP request for a bearer access token.
231         * 
232         * @param request The HTTP request to parse. Must not be {@code null}.
233         * 
234         * @return The bearer access token.
235         * 
236         * @throws ParseException If a bearer access token wasn't found in the
237         *                        HTTP request.
238         */
239        public static DPoPAccessToken parse(final HTTPRequest request)
240                throws ParseException {
241
242                // See http://tools.ietf.org/html/rfc6750#section-2
243                String authzHeader = request.getAuthorization();
244
245                if (authzHeader != null) {
246                        return parse(authzHeader);
247                }
248
249                // Try alternative token locations, form and query string are
250                // parameters are not differentiated here
251                Map<String,List<String>> params = request.getQueryParameters();
252                return parse(params);
253        }
254}