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) (draft-ietf-oauth-dpop-16) 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}