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