Skip to content

Commit

Permalink
fix 457 Support for Bearer Auth (#1930)
Browse files Browse the repository at this point in the history
* fix #457 by introducing an HttpBearerAuth object

See OpenAPITools/openapi-generator#457
Also OpenAPITools/openapi-generator#1446 for typescript, OpenAPITools/openapi-generator#1577 for python

Specs defined as follows currently generate BasicAuth and send an "Authorization: Basic [base64Encode(username + ":" + password)]" header
    components:
      securitySchemes:
        bearer:
          type: http
          scheme: bearer

This change will generate code which uses a new HttpBearerAuth class, which will send a "Authorization: [scheme] [accessToken]" header.
This change is slightly larger and more impactful than simply using OAuth for bearerBearer, but it allows for scheme values other than bearer.

This fix was enabled by the recent commit of OpenAPITools/openapi-generator@80ca67c

This PR is an alternative to OpenAPITools/openapi-generator#1972

* update petstore samples

* Update HttpBearerAuth mustache templates and samples

* correct the expected number of generated java client files

* update the retrofit2 HttpBearerAuth template and samples

* Add resttemplate-specific HttpBearerAuth mustache and samples

* add vertx-specific HttpBearerAuth template and samples

* add java webclient-specific HttpBearerAuth template and samples
  • Loading branch information
davidwcarlson authored and wing328 committed Feb 9, 2019
1 parent 810855a commit bf24da1
Show file tree
Hide file tree
Showing 9 changed files with 324 additions and 4 deletions.
20 changes: 18 additions & 2 deletions .generator/templates/ApiClient.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import java.text.DateFormat;

import {{invokerPackage}}.auth.Authentication;
import {{invokerPackage}}.auth.HttpBasicAuth;
import {{invokerPackage}}.auth.HttpBearerAuth;
import {{invokerPackage}}.auth.ApiKeyAuth;
{{#hasOAuthMethods}}
import {{invokerPackage}}.auth.OAuth;
Expand Down Expand Up @@ -102,8 +103,9 @@ public class ApiClient {
setUserAgent("{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}OpenAPI-Generator/{{{artifactVersion}}}/java{{/httpUserAgent}}");

// Setup authentications (key: authentication name, value: authentication).
authentications = new HashMap<String, Authentication>();{{#authMethods}}{{#isBasic}}
authentications.put("{{name}}", new HttpBasicAuth());{{/isBasic}}{{#isApiKey}}
authentications = new HashMap<String, Authentication>();{{#authMethods}}{{#isBasic}}{{#isBasicBasic}}
authentications.put("{{name}}", new HttpBasicAuth());{{/isBasicBasic}}{{^isBasicBasic}}
authentications.put("{{name}}", new HttpBearerAuth("{{scheme}}"));{{/isBasicBasic}}{{/isBasic}}{{#isApiKey}}
authentications.put("{{name}}", new ApiKeyAuth({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{^isKeyInHeader}}"query"{{/isKeyInHeader}}, "{{keyParamName}}"));{{/isApiKey}}{{#isOAuth}}
authentications.put("{{name}}", new OAuth());{{/isOAuth}}{{/authMethods}}
// Prevent the authentications from being modified.
Expand Down Expand Up @@ -279,6 +281,20 @@ public class ApiClient {
}

{{/hasOAuthMethods}}
/**
* Helper method to set access token for the first Bearer authentication.
* @param bearerToken Bearer token
*/
public void setBearerToken(String bearerToken) {
for (Authentication auth : authentications.values()) {
if (auth instanceof HttpBearerAuth) {
((HttpBearerAuth) auth).setBearerToken(bearerToken);
return;
}
}
throw new RuntimeException("No Bearer authentication configured!");
}

/**
* Set the User-Agent header's value (by adding to the default header map).
* @param userAgent User agent
Expand Down
45 changes: 45 additions & 0 deletions .generator/templates/auth/HttpBearerAuth.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{{>licenseInfo}}

package {{invokerPackage}}.auth;

import {{invokerPackage}}.Pair;

import java.util.Map;
import java.util.List;

{{>generatedAnnotation}}
public class HttpBearerAuth implements Authentication {
private final String scheme;
private String bearerToken;
public HttpBearerAuth(String scheme) {
this.scheme = scheme;
}

/**
* Gets the token, which together with the scheme, will be sent as the value of the Authorization header.
*/
public String getBearerToken() {
return bearerToken;
}

/**
* Sets the token, which together with the scheme, will be sent as the value of the Authorization header.
*/
public void setBearerToken(String bearerToken) {
this.bearerToken = bearerToken;
}

@Override
public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams) {
if(bearerToken == null) {
return;
}

headerParams.put("Authorization", (scheme != null ? upperCaseBearer(scheme) + " " : "") + bearerToken);
}

private static String upperCaseBearer(String scheme) {
return ("bearer".equalsIgnoreCase(scheme)) ? "Bearer" : scheme;
}
}
43 changes: 43 additions & 0 deletions .generator/templates/libraries/feign/auth/HttpBearerAuth.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package {{invokerPackage}}.auth;

import feign.RequestInterceptor;
import feign.RequestTemplate;

/**
* An interceptor that adds the request header needed to use HTTP bearer authentication.
*/
public class HttpBearerAuth implements RequestInterceptor {
private final String scheme;
private String bearerToken;
public HttpBearerAuth(String scheme) {
this.scheme = scheme;
}

/**
* Gets the token, which together with the scheme, will be sent as the value of the Authorization header.
*/
public String getBearerToken() {
return bearerToken;
}

/**
* Sets the token, which together with the scheme, will be sent as the value of the Authorization header.
*/
public void setBearerToken(String bearerToken) {
this.bearerToken = bearerToken;
}

@Override
public void apply(RequestTemplate template) {
if(bearerToken == null) {
return;
}

template.header("Authorization", (scheme != null ? upperCaseBearer(scheme) + " " : "") + bearerToken);
}

private static String upperCaseBearer(String scheme) {
return ("bearer".equalsIgnoreCase(scheme)) ? "Bearer" : scheme;
}
}
20 changes: 18 additions & 2 deletions .generator/templates/libraries/jersey2/ApiClient.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import java.util.regex.Pattern;

import {{invokerPackage}}.auth.Authentication;
import {{invokerPackage}}.auth.HttpBasicAuth;
import {{invokerPackage}}.auth.HttpBearerAuth;
import {{invokerPackage}}.auth.ApiKeyAuth;
{{#hasOAuthMethods}}
import {{invokerPackage}}.auth.OAuth;
Expand Down Expand Up @@ -84,8 +85,9 @@ public class ApiClient {
setUserAgent("{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}OpenAPI-Generator/{{{artifactVersion}}}/java{{/httpUserAgent}}");
// Setup authentications (key: authentication name, value: authentication).
authentications = new HashMap<String, Authentication>();{{#authMethods}}{{#isBasic}}
authentications.put("{{name}}", new HttpBasicAuth());{{/isBasic}}{{#isApiKey}}
authentications = new HashMap<String, Authentication>();{{#authMethods}}{{#isBasic}}{{#isBasicBasic}}
authentications.put("{{name}}", new HttpBasicAuth());{{/isBasicBasic}}{{^isBasicBasic}}
authentications.put("{{name}}", new HttpBearerAuth("{{scheme}}"));{{/isBasicBasic}}{{/isBasic}}{{#isApiKey}}
authentications.put("{{name}}", new ApiKeyAuth({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{^isKeyInHeader}}"query"{{/isKeyInHeader}}, "{{keyParamName}}"));{{/isApiKey}}{{#isOAuth}}
authentications.put("{{name}}", new OAuth());{{/isOAuth}}{{/authMethods}}
// Prevent the authentications from being modified.
Expand Down Expand Up @@ -193,6 +195,20 @@ public class ApiClient {
}

{{#hasOAuthMethods}}
/**
* Helper method to set bearer token for the first Bearer authentication.
* @param bearerToken Bearer token
*/
public void setBearerToken(String bearerToken) {
for (Authentication auth : authentications.values()) {
if (auth instanceof HttpBearerAuth) {
((HttpBearerAuth) auth).setBearerToken(bearerToken);
return;
}
}
throw new RuntimeException("No Bearer authentication configured!");
}

/**
* Helper method to set access token for the first OAuth2 authentication.
* @param accessToken Access token
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package {{invokerPackage}}.auth;

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;

import org.springframework.http.HttpHeaders;
import org.springframework.util.Base64Utils;
import org.springframework.util.MultiValueMap;

{{>generatedAnnotation}}
public class HttpBearerAuth implements Authentication {
private final String scheme;
private String bearerToken;
public HttpBearerAuth(String scheme) {
this.scheme = scheme;
}

public String getBearerToken() {
return bearerToken;
}

public void setBearerToken(String bearerToken) {
this.bearerToken = bearerToken;
}

@Override
public void applyToParams(MultiValueMap<String, String> queryParams, HttpHeaders headerParams) {
if (bearerToken == null) {
return;
}
headerParams.add(HttpHeaders.AUTHORIZATION, (scheme != null ? upperCaseBearer(scheme) + " " : "") + bearerToken);
}

private static String upperCaseBearer(String scheme) {
return ("bearer".equalsIgnoreCase(scheme)) ? "Bearer" : scheme;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package {{invokerPackage}}.auth;

import java.io.IOException;

import com.squareup.okhttp.Interceptor;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;

public class HttpBearerAuth implements Interceptor {
private final String scheme;
private String bearerToken;
public HttpBearerAuth(String scheme) {
this.scheme = scheme;
}

public String getBearerToken() {
return bearerToken;
}

public void setBearerToken(String bearerToken) {
this.bearerToken = bearerToken;
}

@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// If the request already have an authorization (eg. Basic auth), do nothing
if (request.header("Authorization") == null && bearerToken != null) {
request = request.newBuilder()
.addHeader("Authorization", (scheme != null ? upperCaseBearer(scheme) + " " : "") + bearerToken)
.build();
}
return chain.proceed(request);
}

private static String upperCaseBearer(String scheme) {
return ("bearer".equalsIgnoreCase(scheme)) ? "Bearer" : scheme;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package {{invokerPackage}}.auth;

import java.io.IOException;

import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;

public class HttpBearerAuth implements Interceptor {
private final String scheme;
private String bearerToken;
public HttpBearerAuth(String scheme) {
this.scheme = scheme;
}

public String getBearerToken() {
return bearerToken;
}

public void setBearerToken(String bearerToken) {
this.bearerToken = bearerToken;
}

@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// If the request already have an authorization (eg. Basic auth), do nothing
if (request.header("Authorization") == null && bearerToken != null) {
request = request.newBuilder()
.addHeader("Authorization", (scheme != null ? upperCaseBearer(scheme) + " " : "") + bearerToken)
.build();
}
return chain.proceed(request);
}

private static String upperCaseBearer(String scheme) {
return ("bearer".equalsIgnoreCase(scheme)) ? "Bearer" : scheme;
}

}
39 changes: 39 additions & 0 deletions .generator/templates/libraries/vertx/auth/HttpBearerAuth.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{{>licenseInfo}}

package {{invokerPackage}}.auth;

import {{invokerPackage}}.Pair;
import io.vertx.core.MultiMap;
import java.util.Base64;
import java.nio.charset.StandardCharsets;
import java.util.List;

{{>generatedAnnotation}}
public class HttpBearerAuth implements Authentication {
private final String scheme;
private String bearerToken;
public HttpBearerAuth(String scheme) {
this.scheme = scheme;
}

public String getBearerToken() {
return bearerToken;
}

public void setBearerToken(String bearerToken) {
this.bearerToken = bearerToken;
}

@Override
public void applyToParams(List<Pair> queryParams, MultiMap headerParams) {
if (bearerToken == null) {
return;
}
headerParams.add("Authorization", (scheme != null ? upperCaseBearer(scheme) + " " : "") + bearerToken);
}

private static String upperCaseBearer(String scheme) {
return ("bearer".equalsIgnoreCase(scheme)) ? "Bearer" : scheme;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package {{invokerPackage}}.auth;

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;

import org.springframework.http.HttpHeaders;
import org.springframework.util.Base64Utils;
import org.springframework.util.MultiValueMap;

{{>generatedAnnotation}}
public class HttpBearerAuth implements Authentication {
private final String scheme;
private String bearerToken;
public HttpBearerAuth(String scheme) {
this.scheme = scheme;
}

public String getBearerToken() {
return bearerToken;
}

public void setBearerToken(String bearerToken) {
this.bearerToken = bearerToken;
}

@Override
public void applyToParams(MultiValueMap<String, String> queryParams, HttpHeaders headerParams) {
if (bearerToken == null) {
return;
}
headerParams.add(HttpHeaders.AUTHORIZATION, (scheme != null ? upperCaseBearer(scheme) + " " : "") + bearerToken);
}

private static String upperCaseBearer(String scheme) {
return ("bearer".equalsIgnoreCase(scheme)) ? "Bearer" : scheme;
}

}

0 comments on commit bf24da1

Please sign in to comment.