1414package pl .net .was .authentication ;
1515
1616import com .fasterxml .jackson .annotation .JsonProperty ;
17- import com .google .common .base .Suppliers ;
17+ import com .google .common .cache .CacheBuilder ;
18+ import com .google .common .cache .CacheLoader ;
19+ import com .google .common .cache .LoadingCache ;
1820import com .google .common .collect .ImmutableMap ;
1921import com .google .inject .Inject ;
20- import io .airlift .http .client .BodyGenerator ;
2122import io .airlift .http .client .HttpClient ;
2223import io .airlift .http .client .HttpRequestFilter ;
2324import io .airlift .http .client .Request ;
2425import io .swagger .v3 .oas .models .PathItem ;
26+ import io .swagger .v3 .oas .models .security .OAuthFlow ;
27+ import io .swagger .v3 .oas .models .security .OAuthFlows ;
2528import io .swagger .v3 .oas .models .security .SecurityRequirement ;
2629import io .swagger .v3 .oas .models .security .SecurityScheme ;
2730import pl .net .was .OpenApiConfig ;
3336import java .util .List ;
3437import java .util .Locale ;
3538import java .util .Map ;
36- import java .util .function .Supplier ;
3739
40+ import static com .google .common .base .MoreObjects .firstNonNull ;
3841import static com .google .common .io .BaseEncoding .base64Url ;
39- import static io .airlift .http .client .HttpUriBuilder .uriBuilderFrom ;
4042import static io .airlift .http .client .JsonResponseHandler .createJsonResponseHandler ;
4143import static io .airlift .http .client .Request .Builder .fromRequest ;
4244import static io .airlift .http .client .Request .Builder .preparePost ;
@@ -66,11 +68,10 @@ public class Authentication
6668
6769 private final URI baseUri ;
6870 private final HttpClient httpClient ;
69- private final BodyGenerator bodyGenerator ;
70- private final String tokenEndpoint ;
7171 private final String clientId ;
7272 private final String clientSecret ;
73- private final Supplier <String > token = Suppliers .memoize (this ::getToken );
73+ private final LoadingCache <String , String > tokens = CacheBuilder .newBuilder ()
74+ .build (CacheLoader .from (this ::getToken ));
7475
7576 @ Inject
7677 public Authentication (OpenApiConfig config ,
@@ -93,13 +94,6 @@ public Authentication(OpenApiConfig config,
9394
9495 this .baseUri = requireNonNull (config .getBaseUri (), "baseUri is null" );
9596 this .httpClient = requireNonNull (httpClient , "httpClient is null" );
96- if (config .getGrantType () != null && !config .getGrantType ().isEmpty ()) {
97- this .bodyGenerator = createStaticBodyGenerator (getBody (config .getGrantType (), config .getUsername (), config .getPassword ()), UTF_8 );
98- }
99- else {
100- this .bodyGenerator = null ;
101- }
102- this .tokenEndpoint = config .getTokenEndpoint ();
10397 this .clientId = config .getClientId ();
10498 this .clientSecret = config .getClientSecret ();
10599 }
@@ -120,7 +114,7 @@ public Request filterRequest(Request request)
120114 applyApiKeyAuth (builder , uri , scheme );
121115 }
122116 case HTTP -> applyHttpAuth (builder , null );
123- case OAUTH -> applyOAuth ( builder );
117+ case OAUTH -> throw new UnsupportedOperationException ( "OAuth cannot be used as a default authentication method" );
124118 }
125119 }
126120 return builder .build ();
@@ -150,7 +144,17 @@ private void applyAuthFilters(Request.Builder builder, List<SecurityRequirement>
150144 switch (securitySchema .getType ()) {
151145 case APIKEY -> applyApiKeyAuth (builder , uri , securitySchema );
152146 case HTTP -> applyHttpAuth (builder , securitySchema .getScheme ());
153- case OAUTH2 -> applyOAuth (builder );
147+ case OAUTH2 -> {
148+ OAuthFlows flows = requireNonNull (securitySchema .getFlows (), "flows are null" );
149+ OAuthFlow flow = firstNonNull (
150+ flows .getPassword (),
151+ firstNonNull (
152+ flows .getAuthorizationCode (),
153+ firstNonNull (
154+ flows .getClientCredentials (),
155+ flows .getImplicit ())));
156+ applyOAuth (builder , flow .getAuthorizationUrl ());
157+ }
154158 default -> throw new IllegalArgumentException (format ("Unsupported security schema %s" , securitySchema .getType ()));
155159 }
156160 });
@@ -208,7 +212,7 @@ private Request.Builder applyHttpAuth(Request.Builder builder, String scheme)
208212 return builder .addHeader ("Authorization" , value );
209213 }
210214
211- private Request .Builder applyOAuth (Request .Builder builder )
215+ private Request .Builder applyOAuth (Request .Builder builder , String authorizationUrl )
212216 {
213217 // TODO pick one of supported securitySchema.getFlows(), instead of hardcoding clientCredentials
214218 // TODO use options as scopes
@@ -227,7 +231,7 @@ private Request.Builder applyOAuth(Request.Builder builder)
227231 write:pets: modify pets in your account
228232 read:pets: read your pets
229233 */
230- return builder .addHeader ("Authorization" , "Bearer " + token . get ( ));
234+ return builder .addHeader ("Authorization" , "Bearer " + tokens . getUnchecked ( authorizationUrl ));
231235 }
232236
233237 private static String getAuthHeader (String scheme , String username , String password )
@@ -240,32 +244,30 @@ private static String capitalize(String input)
240244 return input .substring (0 , 1 ).toUpperCase (Locale .ENGLISH ) + input .substring (1 ).toLowerCase (Locale .ENGLISH );
241245 }
242246
243- private String getToken ()
247+ private String getToken (String authorizationUrl )
244248 {
245- requireNonNull (bodyGenerator , "bodyGenerator is null" );
246249 return httpClient .execute (
247250 preparePost ()
248- .setUri (uriBuilderFrom (baseUri )
249- .replacePath (tokenEndpoint )
250- .build ())
251+ .setUri (URI .create (authorizationUrl ))
251252 .setHeader ("Content-Type" , "application/x-www-form-urlencoded" )
252- .setHeader ("Authorization" , "Basic " + base64Url ().encode ("%s:%s" .formatted (clientId , clientSecret ).getBytes (UTF_8 )))
253- .setBodyGenerator (bodyGenerator )
253+ .setBodyGenerator (createStaticBodyGenerator (
254+ getBody ("client_credentials" , clientId , clientSecret ),
255+ UTF_8 ))
254256 .build (),
255257 createJsonResponseHandler (jsonCodec (Authentication .TokenResponse .class )))
256258 .accessToken ();
257259 }
258260
259- private static String getBody (String grantType , String username , String password )
261+ private static String getBody (String grantType , String clientId , String clientSecret )
260262 {
261263 requireNonNull (grantType , "grantType is null" );
262264 ImmutableMap .Builder <String , String > params = ImmutableMap .<String , String >builder ()
263265 .put ("grant_type" , grantType );
264- if (username != null && !username .isEmpty ()) {
265- params .put ("username " , username );
266+ if (clientId != null && !clientId .isEmpty ()) {
267+ params .put ("client_id " , clientId );
266268 }
267- if (password != null && !password .isEmpty ()) {
268- params .put ("password " , password );
269+ if (clientSecret != null && !clientSecret .isEmpty ()) {
270+ params .put ("client_secret " , clientSecret );
269271 }
270272
271273 return params .build ().entrySet ().stream ()
0 commit comments