View Javadoc
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 }