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}