1 package com.guinetik.rr.auth;
2
3 import java.util.HashMap;
4 import java.util.Map;
5
6 /**
7 * Authentication strategy that implements OAuth 2.0 password flow.
8 * This strategy gets and refreshes OAuth 2.0 access tokens using username and password.
9 */
10 public class OAuth2PasswordStrategy extends AbstractOAuth2Strategy {
11
12 private final String username;
13 private final String password;
14 private final String clientId;
15 private final String clientSecret;
16 private String refreshToken;
17
18 /**
19 * Creates a new OAuth 2.0 password strategy.
20 *
21 * @param username the user's username
22 * @param password the user's password
23 * @param clientId the OAuth 2.0 client ID (optional, can be null)
24 * @param clientSecret the OAuth 2.0 client secret (optional, can be null)
25 * @param tokenUrl the OAuth 2.0 token endpoint URL
26 */
27 public OAuth2PasswordStrategy(String username, String password, String clientId, String clientSecret, String tokenUrl) {
28 this(username, password, clientId, clientSecret, tokenUrl, new HashMap<>());
29 }
30
31 /**
32 * Creates a new OAuth 2.0 password strategy with additional parameters.
33 *
34 * @param username the user's username
35 * @param password the user's password
36 * @param clientId the OAuth 2.0 client ID (optional, can be null)
37 * @param clientSecret the OAuth 2.0 client secret (optional, can be null)
38 * @param tokenUrl the OAuth 2.0 token endpoint URL
39 * @param additionalParams additional parameters to include in the token request
40 */
41 public OAuth2PasswordStrategy(String username, String password, String clientId, String clientSecret,
42 String tokenUrl,
43 Map<String, String> additionalParams) {
44 super(tokenUrl, additionalParams);
45 this.username = username;
46 this.password = password;
47 this.clientId = clientId;
48 this.clientSecret = clientSecret;
49 }
50
51 @Override
52 public AuthType getType() {
53 return AuthType.OAUTH_PASSWORD;
54 }
55
56 /**
57 * {@inheritDoc}
58 * @throws TokenRefreshException if the username or password is not provided.
59 */
60 @Override
61 protected void validateCredentials() {
62 if (username == null || password == null) {
63 throw new TokenRefreshException("Username and Password are required for OAuth2 password flow");
64 }
65 }
66
67 /**
68 * {@inheritDoc}
69 * <p>
70 * Prepares parameters for the OAuth 2.0 password grant type or refresh token grant type.
71 * If a refresh token is available, it will be used. Otherwise, username and password will be used.
72 * Client ID and client secret are included if provided.
73 */
74 @Override
75 protected Map<String, String> prepareTokenRequestParams() {
76 Map<String, String> formParams = new HashMap<>();
77
78 // If we have a refresh token, use that; otherwise, use password flow
79 if (this.refreshToken != null && !this.refreshToken.isEmpty()) {
80 formParams.put("grant_type", "refresh_token");
81 formParams.put("refresh_token", this.refreshToken);
82 } else {
83 formParams.put("grant_type", "password");
84 formParams.put("username", username);
85 formParams.put("password", password);
86 }
87
88 // Add client credentials if provided
89 if (clientId != null && !clientId.isEmpty()) {
90 formParams.put("client_id", clientId);
91
92 if (clientSecret != null && !clientSecret.isEmpty()) {
93 formParams.put("client_secret", clientSecret);
94 }
95 }
96
97 return formParams;
98 }
99
100 /**
101 * {@inheritDoc}
102 * <p>
103 * Processes the token response, extracting and storing the refresh_token if present,
104 * in addition to the access_token and expiry time handled by the superclass.
105 */
106 @Override
107 protected boolean processTokenResponse(Map<String, Object> tokenResponse) {
108 Object refreshTokenObj = tokenResponse.get("refresh_token");
109
110 // Update refresh token if provided
111 if (refreshTokenObj != null) {
112 this.refreshToken = refreshTokenObj.toString();
113 }
114
115 // Let the parent class handle the rest
116 return super.processTokenResponse(tokenResponse);
117 }
118
119 /**
120 * Gets the current refresh token.
121 *
122 * @return the current refresh token, or null if not yet obtained
123 */
124 public String getRefreshToken() {
125 return refreshToken;
126 }
127 }