001package com.box.sdkgen.box.jwtauth; 002 003import static com.box.sdkgen.internal.utils.UtilsManager.createJwtAssertion; 004import static com.box.sdkgen.internal.utils.UtilsManager.entryOf; 005import static com.box.sdkgen.internal.utils.UtilsManager.getEpochTimeInSeconds; 006import static com.box.sdkgen.internal.utils.UtilsManager.getUuid; 007import static com.box.sdkgen.internal.utils.UtilsManager.isBrowser; 008import static com.box.sdkgen.internal.utils.UtilsManager.mapOf; 009 010import com.box.sdkgen.box.errors.BoxSDKError; 011import com.box.sdkgen.box.tokenstorage.InMemoryTokenStorage; 012import com.box.sdkgen.box.tokenstorage.TokenStorage; 013import com.box.sdkgen.internal.utils.JwtKey; 014import com.box.sdkgen.internal.utils.JwtSignOptions; 015import com.box.sdkgen.managers.authorization.AuthorizationManager; 016import com.box.sdkgen.networking.auth.Authentication; 017import com.box.sdkgen.networking.network.NetworkSession; 018import com.box.sdkgen.schemas.accesstoken.AccessToken; 019import com.box.sdkgen.schemas.postoauth2revoke.PostOAuth2Revoke; 020import com.box.sdkgen.schemas.postoauth2token.PostOAuth2Token; 021import com.box.sdkgen.schemas.postoauth2token.PostOAuth2TokenGrantTypeField; 022import com.box.sdkgen.schemas.postoauth2token.PostOAuth2TokenSubjectTokenTypeField; 023import java.util.List; 024import java.util.Map; 025 026public class BoxJWTAuth implements Authentication { 027 028 /** An object containing all JWT configuration to use for authentication */ 029 public final JWTConfig config; 030 031 /** 032 * An object responsible for storing token. If no custom implementation provided, the token will 033 * be stored in memory. 034 */ 035 public final TokenStorage tokenStorage; 036 037 /** 038 * The ID of the user or enterprise to authenticate as. If not provided, defaults to the 039 * enterprise ID if set, otherwise defaults to the user ID. 040 */ 041 public String subjectId; 042 043 /** The type of the subject ID provided. Must be either 'user' or 'enterprise'. */ 044 public String subjectType; 045 046 public BoxJWTAuth(JWTConfig config) { 047 this.config = config; 048 this.tokenStorage = this.config.getTokenStorage(); 049 this.subjectId = 050 (!(this.config.getEnterpriseId() == null) 051 ? this.config.getEnterpriseId() 052 : this.config.getUserId()); 053 this.subjectType = (!(this.config.getEnterpriseId() == null) ? "enterprise" : "user"); 054 } 055 056 /** Get new access token using JWT auth. */ 057 public AccessToken refreshToken() { 058 return refreshToken(null); 059 } 060 061 /** 062 * Get new access token using JWT auth. 063 * 064 * @param networkSession An object to keep network session state 065 */ 066 @Override 067 public AccessToken refreshToken(NetworkSession networkSession) { 068 if (isBrowser()) { 069 throw new BoxSDKError("JWT auth is not supported in browser environment."); 070 } 071 Map<String, Object> claims = 072 mapOf( 073 entryOf("exp", getEpochTimeInSeconds() + 30), 074 entryOf("box_sub_type", this.subjectType)); 075 JwtSignOptions jwtOptions = 076 new JwtSignOptions( 077 this.config.getAlgorithm(), 078 "https://api.box.com/oauth2/token", 079 this.config.getClientId(), 080 this.subjectId, 081 getUuid(), 082 this.config.getJwtKeyId(), 083 this.config.getPrivateKeyDecryptor()); 084 JwtKey jwtKey = new JwtKey(this.config.getPrivateKey(), this.config.getPrivateKeyPassphrase()); 085 String assertion = createJwtAssertion(claims, jwtKey, jwtOptions); 086 AuthorizationManager authManager = 087 new AuthorizationManager.Builder() 088 .networkSession((!(networkSession == null) ? networkSession : new NetworkSession())) 089 .build(); 090 AccessToken token = 091 authManager.requestAccessToken( 092 new PostOAuth2Token.Builder( 093 PostOAuth2TokenGrantTypeField.URN_IETF_PARAMS_OAUTH_GRANT_TYPE_JWT_BEARER) 094 .assertion(assertion) 095 .clientId(this.config.getClientId()) 096 .clientSecret(this.config.getClientSecret()) 097 .build()); 098 this.tokenStorage.store(token); 099 return token; 100 } 101 102 /** 103 * Get the current access token. If the current access token is expired or not found, this method 104 * will attempt to refresh the token. 105 */ 106 public AccessToken retrieveToken() { 107 return retrieveToken(null); 108 } 109 110 /** 111 * Get the current access token. If the current access token is expired or not found, this method 112 * will attempt to refresh the token. 113 * 114 * @param networkSession An object to keep network session state 115 */ 116 @Override 117 public AccessToken retrieveToken(NetworkSession networkSession) { 118 AccessToken oldToken = this.tokenStorage.get(); 119 if (oldToken == null) { 120 AccessToken newToken = this.refreshToken(networkSession); 121 return newToken; 122 } 123 return oldToken; 124 } 125 126 public String retrieveAuthorizationHeader() { 127 return retrieveAuthorizationHeader(null); 128 } 129 130 @Override 131 public String retrieveAuthorizationHeader(NetworkSession networkSession) { 132 AccessToken token = this.retrieveToken(networkSession); 133 return String.join("", "Bearer ", token.getAccessToken()); 134 } 135 136 /** 137 * Create a new BoxJWTAuth instance that uses the provided user ID as the subject of the JWT 138 * assertion. May be one of this application's created App User. Depending on the configured User 139 * Access Level, may also be any other App User or Managed User in the enterprise. 140 * <https://developer.box.com/en/guides/applications/> 141 * <https://developer.box.com/en/guides/authentication/select/> 142 * 143 * @param userId The id of the user to authenticate 144 */ 145 public BoxJWTAuth withUserSubject(String userId) { 146 return withUserSubject(userId, new InMemoryTokenStorage()); 147 } 148 149 /** 150 * Create a new BoxJWTAuth instance that uses the provided user ID as the subject of the JWT 151 * assertion. May be one of this application's created App User. Depending on the configured User 152 * Access Level, may also be any other App User or Managed User in the enterprise. 153 * <https://developer.box.com/en/guides/applications/> 154 * <https://developer.box.com/en/guides/authentication/select/> 155 * 156 * @param userId The id of the user to authenticate 157 * @param tokenStorage Object responsible for storing token in newly created BoxJWTAuth. If no 158 * custom implementation provided, the token will be stored in memory. 159 */ 160 public BoxJWTAuth withUserSubject(String userId, TokenStorage tokenStorage) { 161 JWTConfig newConfig = 162 new JWTConfig.Builder( 163 this.config.getClientId(), 164 this.config.getClientSecret(), 165 this.config.getJwtKeyId(), 166 this.config.getPrivateKey(), 167 this.config.getPrivateKeyPassphrase()) 168 .enterpriseId(null) 169 .userId(userId) 170 .tokenStorage(tokenStorage) 171 .build(); 172 BoxJWTAuth newAuth = new BoxJWTAuth(newConfig); 173 return newAuth; 174 } 175 176 /** 177 * Create a new BoxJWTAuth instance that uses the provided enterprise ID as the subject of the JWT 178 * assertion. 179 * 180 * @param enterpriseId The id of the enterprise to authenticate 181 */ 182 public BoxJWTAuth withEnterpriseSubject(String enterpriseId) { 183 return withEnterpriseSubject(enterpriseId, new InMemoryTokenStorage()); 184 } 185 186 /** 187 * Create a new BoxJWTAuth instance that uses the provided enterprise ID as the subject of the JWT 188 * assertion. 189 * 190 * @param enterpriseId The id of the enterprise to authenticate 191 * @param tokenStorage Object responsible for storing token in newly created BoxJWTAuth. If no 192 * custom implementation provided, the token will be stored in memory. 193 */ 194 public BoxJWTAuth withEnterpriseSubject(String enterpriseId, TokenStorage tokenStorage) { 195 JWTConfig newConfig = 196 new JWTConfig.Builder( 197 this.config.getClientId(), 198 this.config.getClientSecret(), 199 this.config.getJwtKeyId(), 200 this.config.getPrivateKey(), 201 this.config.getPrivateKeyPassphrase()) 202 .enterpriseId(enterpriseId) 203 .userId(null) 204 .tokenStorage(tokenStorage) 205 .build(); 206 BoxJWTAuth newAuth = new BoxJWTAuth(newConfig); 207 return newAuth; 208 } 209 210 /** 211 * Downscope access token to the provided scopes. Returning a new access token with the provided 212 * scopes, with the original access token unchanged. 213 * 214 * @param scopes The scope(s) to apply to the resulting token. 215 * @param resource The file or folder to get a downscoped token for. If None and shared_link None, 216 * the resulting token will not be scoped down to just a single item. The resource should be a 217 * full URL to an item, e.g. https://api.box.com/2.0/files/123456. 218 * @param sharedLink The shared link to get a downscoped token for. If None and item None, the 219 * resulting token will not be scoped down to just a single item. 220 * @param networkSession An object to keep network session state 221 */ 222 @Override 223 public AccessToken downscopeToken( 224 List<String> scopes, String resource, String sharedLink, NetworkSession networkSession) { 225 AccessToken token = this.retrieveToken(networkSession); 226 if (token == null) { 227 throw new BoxSDKError( 228 "No access token is available. Make an API call to retrieve a token before calling this method."); 229 } 230 AuthorizationManager authManager = 231 new AuthorizationManager.Builder() 232 .networkSession((!(networkSession == null) ? networkSession : new NetworkSession())) 233 .build(); 234 AccessToken downscopedToken = 235 authManager.requestAccessToken( 236 new PostOAuth2Token.Builder( 237 PostOAuth2TokenGrantTypeField.URN_IETF_PARAMS_OAUTH_GRANT_TYPE_TOKEN_EXCHANGE) 238 .subjectToken(token.getAccessToken()) 239 .subjectTokenType( 240 PostOAuth2TokenSubjectTokenTypeField 241 .URN_IETF_PARAMS_OAUTH_TOKEN_TYPE_ACCESS_TOKEN) 242 .resource(resource) 243 .scope(String.join(" ", scopes)) 244 .boxSharedLink(sharedLink) 245 .build()); 246 return downscopedToken; 247 } 248 249 /** Revoke the current access token and remove it from token storage. */ 250 public void revokeToken() { 251 revokeToken(null); 252 } 253 254 /** 255 * Revoke the current access token and remove it from token storage. 256 * 257 * @param networkSession An object to keep network session state 258 */ 259 @Override 260 public void revokeToken(NetworkSession networkSession) { 261 AccessToken oldToken = this.tokenStorage.get(); 262 if (oldToken == null) { 263 return; 264 } 265 AuthorizationManager authManager = 266 new AuthorizationManager.Builder() 267 .networkSession((!(networkSession == null) ? networkSession : new NetworkSession())) 268 .build(); 269 authManager.revokeAccessToken( 270 new PostOAuth2Revoke.Builder() 271 .clientId(this.config.getClientId()) 272 .clientSecret(this.config.getClientSecret()) 273 .token(oldToken.getAccessToken()) 274 .build()); 275 this.tokenStorage.clear(); 276 } 277 278 public JWTConfig getConfig() { 279 return config; 280 } 281 282 public TokenStorage getTokenStorage() { 283 return tokenStorage; 284 } 285}