Skip to content

Commit 265de96

Browse files
committed
add authorizations to retrofit client
added basic auth, api key and oauth support to the service generator using okhttp interceptors Fix #962
1 parent 28579ce commit 265de96

File tree

24 files changed

+1629
-106
lines changed

24 files changed

+1629
-106
lines changed

modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/RetrofitClientCodegen.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ public RetrofitClientCodegen() {
5252
supportingFiles.add(new SupportingFile("pom.mustache", "", "pom.xml"));
5353
supportingFiles.add(new SupportingFile("service.mustache",
5454
(sourceFolder + File.separator + invokerPackage).replace(".", java.io.File.separator), "ServiceGenerator.java"));
55+
supportingFiles.add(new SupportingFile("auth/basic.mustache",
56+
(sourceFolder + File.separator + invokerPackage + File.separator + "auth").replace(".", java.io.File.separator), "BasicAuthorization.java"));
57+
supportingFiles.add(new SupportingFile("auth/apikey.mustache",
58+
(sourceFolder + File.separator + invokerPackage + File.separator + "auth").replace(".", java.io.File.separator), "ApiKeyAuthorization.java"));
59+
supportingFiles.add(new SupportingFile("auth/oauth.mustache",
60+
(sourceFolder + File.separator + invokerPackage + File.separator + "auth").replace(".", java.io.File.separator), "OauthAuthorization.java"));
61+
supportingFiles.add(new SupportingFile("auth/oauthflow.mustache",
62+
(sourceFolder + File.separator + invokerPackage + File.separator + "auth").replace(".", java.io.File.separator), "OauthFlow.java"));
63+
supportingFiles.add(new SupportingFile("auth/oauthokclient.mustache",
64+
(sourceFolder + File.separator + invokerPackage + File.separator + "auth").replace(".", java.io.File.separator), "OauthOkHttpClient.java"));
5565

5666
languageSpecificPrimitives = new HashSet<String>(
5767
Arrays.asList(

modules/swagger-codegen/src/main/resources/retrofit/api.mustache

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package {{package}};
22

33
import {{modelPackage}}.*;
44

5+
import retrofit.Callback;
56
import retrofit.http.*;
67
import retrofit.mime.*;
78
import java.util.*;
@@ -14,6 +15,7 @@ public interface {{classname}} {
1415
{{#operation}}
1516
/**
1617
* {{summary}}
18+
* Sync method
1719
* {{notes}}
1820
{{#allParams}} * @param {{paramName}} {{description}}
1921
{{/allParams}} * @return {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}
@@ -22,8 +24,22 @@ public interface {{classname}} {
2224
{{#isMultipart}}@Multipart{{/isMultipart}}{{^isMultipart}}@FormUrlEncoded{{/isMultipart}}{{/-first}}{{/formParams}}
2325
@{{httpMethod}}("{{path}}")
2426
{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Object{{/returnType}} {{nickname}}({{^allParams}});{{/allParams}}
25-
{{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}}, {{/hasMore}}{{^hasMore}}
26-
);{{/hasMore}}{{/allParams}}
27+
{{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}},{{/hasMore}}{{^hasMore}}
28+
);{{/hasMore}}{{/allParams}}
29+
30+
/**
31+
* {{summary}}
32+
* Async method
33+
{{#allParams}} * @param {{paramName}} {{description}}
34+
{{/allParams}} * @param cb callback method
35+
* @return void
36+
*/
37+
{{#formParams}}{{#-first}}
38+
{{#isMultipart}}@Multipart{{/isMultipart}}{{^isMultipart}}@FormUrlEncoded{{/isMultipart}}{{/-first}}{{/formParams}}
39+
@{{httpMethod}}("{{path}}")
40+
void {{nickname}}(
41+
{{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}},{{/allParams}} Callback<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Object{{/returnType}}> cb
42+
);
2743
{{/operation}}
2844
}
29-
{{/operations}}
45+
{{/operations}}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package {{invokerPackage}}.auth;
2+
3+
import java.io.IOException;
4+
import java.net.URI;
5+
import java.net.URISyntaxException;
6+
7+
import com.squareup.okhttp.Interceptor;
8+
import com.squareup.okhttp.Request;
9+
import com.squareup.okhttp.Response;
10+
11+
public class ApiKeyAuthorization implements Interceptor {
12+
private final String location;
13+
private final String paramName;
14+
15+
private String apiKey;
16+
17+
public ApiKeyAuthorization(String location, String paramName) {
18+
this.location = location;
19+
this.paramName = paramName;
20+
}
21+
22+
public String getLocation() {
23+
return location;
24+
}
25+
26+
public String getParamName() {
27+
return paramName;
28+
}
29+
30+
public String getApiKey() {
31+
return apiKey;
32+
}
33+
34+
public void setApiKey(String apiKey) {
35+
this.apiKey = apiKey;
36+
}
37+
38+
@Override
39+
public Response intercept(Chain chain) throws IOException {
40+
String paramValue;
41+
Request request = chain.request();
42+
43+
if (location == "query") {
44+
String newQuery = request.uri().getQuery();
45+
paramValue = paramName + "=" + apiKey;
46+
if (newQuery == null) {
47+
newQuery = paramValue;
48+
} else {
49+
newQuery += "&" + paramValue;
50+
}
51+
52+
URI newUri;
53+
try {
54+
newUri = new URI(request.uri().getScheme(), request.uri().getAuthority(),
55+
request.uri().getPath(), newQuery, request.uri().getFragment());
56+
} catch (URISyntaxException e) {
57+
throw new IOException(e);
58+
}
59+
60+
request = request.newBuilder().url(newUri.toURL()).build();
61+
} else if (location == "header") {
62+
request = request.newBuilder()
63+
.addHeader(paramName, apiKey)
64+
.build();
65+
}
66+
return chain.proceed(request);
67+
}
68+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package {{invokerPackage}}.auth;
2+
3+
import java.io.IOException;
4+
5+
import com.squareup.okhttp.Credentials;
6+
import com.squareup.okhttp.Interceptor;
7+
import com.squareup.okhttp.Request;
8+
import com.squareup.okhttp.Response;
9+
10+
public class BasicAuthorization implements Interceptor {
11+
12+
private String username;
13+
private String password;
14+
15+
public String getUsername() {
16+
return username;
17+
}
18+
19+
public void setUsername(String username) {
20+
this.username = username;
21+
}
22+
23+
public String getPassword() {
24+
return password;
25+
}
26+
27+
public void setPassword(String password) {
28+
this.password = password;
29+
}
30+
31+
public void setCredentials(String username, String password) {
32+
this.username = username;
33+
this.password = password;
34+
}
35+
36+
@Override
37+
public Response intercept(Chain chain) throws IOException {
38+
Request request = chain.request();
39+
40+
// If the request already have an authorization (eg. Basic auth), do nothing
41+
if (request.header("Authorization") == null) {
42+
String credentials = Credentials.basic(username, password);
43+
request = request.newBuilder()
44+
.addHeader("Authorization", credentials)
45+
.build();
46+
}
47+
return chain.proceed(request);
48+
}
49+
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package {{invokerPackage}}.auth;
2+
3+
import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
4+
5+
import java.io.IOException;
6+
import java.util.Map;
7+
8+
import org.apache.oltu.oauth2.client.OAuthClient;
9+
import org.apache.oltu.oauth2.client.request.OAuthBearerClientRequest;
10+
import org.apache.oltu.oauth2.client.request.OAuthClientRequest;
11+
import org.apache.oltu.oauth2.client.request.OAuthClientRequest.AuthenticationRequestBuilder;
12+
import org.apache.oltu.oauth2.client.request.OAuthClientRequest.TokenRequestBuilder;
13+
import org.apache.oltu.oauth2.client.response.OAuthJSONAccessTokenResponse;
14+
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
15+
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
16+
import org.apache.oltu.oauth2.common.message.types.GrantType;
17+
18+
import com.squareup.okhttp.Interceptor;
19+
import com.squareup.okhttp.OkHttpClient;
20+
import com.squareup.okhttp.Request;
21+
import com.squareup.okhttp.Request.Builder;
22+
import com.squareup.okhttp.Response;
23+
24+
public class OauthAuthorization implements Interceptor {
25+
26+
private volatile String accessToken;
27+
private OAuthClient oauthClient;
28+
29+
private TokenRequestBuilder tokenRequestBuilder;
30+
private AuthenticationRequestBuilder authenticationRequestBuilder;
31+
32+
public OauthAuthorization( OkHttpClient client, TokenRequestBuilder requestBuilder ) {
33+
this.oauthClient = new OAuthClient(new OauthOkHttpClient(client));
34+
this.tokenRequestBuilder = requestBuilder;
35+
}
36+
37+
public OauthAuthorization(TokenRequestBuilder requestBuilder ) {
38+
this(new OkHttpClient(), requestBuilder);
39+
}
40+
41+
public OauthAuthorization(OauthFlow flow, String authorizationUrl, String tokenUrl, String scopes) {
42+
this(OAuthClientRequest.tokenLocation(tokenUrl).setScope(scopes));
43+
setFlow(flow);
44+
authenticationRequestBuilder = OAuthClientRequest.authorizationLocation(authorizationUrl);
45+
}
46+
47+
public void setFlow(OauthFlow flow) {
48+
switch(flow) {
49+
case accessCode:
50+
case implicit:
51+
tokenRequestBuilder.setGrantType(GrantType.AUTHORIZATION_CODE);
52+
break;
53+
case password:
54+
tokenRequestBuilder.setGrantType(GrantType.PASSWORD);
55+
break;
56+
case application:
57+
tokenRequestBuilder.setGrantType(GrantType.CLIENT_CREDENTIALS);
58+
break;
59+
default:
60+
break;
61+
}
62+
}
63+
64+
@Override
65+
public Response intercept(Chain chain)
66+
throws IOException {
67+
68+
Request request = chain.request();
69+
70+
// If the request already have an authorization (eg. Basic auth), do nothing
71+
if (request.header("Authorization") != null) {
72+
return chain.proceed(request);
73+
}
74+
75+
// If first time, get the token
76+
OAuthClientRequest oAuthRequest;
77+
if (getAccessToken() == null) {
78+
updateAccessToken(null);
79+
}
80+
81+
// Build the request
82+
Builder rb = request.newBuilder();
83+
84+
String requestAccessToken = new String(getAccessToken());
85+
try {
86+
oAuthRequest = new OAuthBearerClientRequest(request.urlString())
87+
.setAccessToken(requestAccessToken)
88+
.buildHeaderMessage();
89+
} catch (OAuthSystemException e) {
90+
throw new IOException(e);
91+
}
92+
93+
for ( Map.Entry<String, String> header : oAuthRequest.getHeaders().entrySet() ) {
94+
rb.addHeader(header.getKey(), header.getValue());
95+
}
96+
rb.url( oAuthRequest.getLocationUri());
97+
98+
//Execute the request
99+
Response response = chain.proceed(rb.build());
100+
101+
// 401 most likely indicates that access token has expired.
102+
// Time to refresh and resend the request
103+
if ( response.code() == HTTP_UNAUTHORIZED ) {
104+
updateAccessToken(requestAccessToken);
105+
return intercept( chain );
106+
}
107+
return response;
108+
}
109+
110+
public synchronized void updateAccessToken(String requestAccessToken) throws IOException {
111+
if (getAccessToken() == null || getAccessToken().equals(requestAccessToken)) {
112+
try {
113+
OAuthJSONAccessTokenResponse accessTokenResponse;
114+
accessTokenResponse = oauthClient.accessToken(this.tokenRequestBuilder.buildBodyMessage());
115+
setAccessToken(accessTokenResponse.getAccessToken());
116+
} catch (OAuthSystemException e) {
117+
throw new IOException(e);
118+
} catch (OAuthProblemException e) {
119+
throw new IOException(e);
120+
}
121+
}
122+
}
123+
124+
public synchronized String getAccessToken() {
125+
return accessToken;
126+
}
127+
128+
public synchronized void setAccessToken(String accessToken) {
129+
this.accessToken = accessToken;
130+
}
131+
132+
public TokenRequestBuilder getTokenRequestBuilder() {
133+
return tokenRequestBuilder;
134+
}
135+
136+
public void setTokenRequestBuilder(TokenRequestBuilder tokenRequestBuilder) {
137+
this.tokenRequestBuilder = tokenRequestBuilder;
138+
}
139+
140+
public AuthenticationRequestBuilder getAuthenticationRequestBuilder() {
141+
return authenticationRequestBuilder;
142+
}
143+
144+
public void setAuthenticationRequestBuilder(AuthenticationRequestBuilder authenticationRequestBuilder) {
145+
this.authenticationRequestBuilder = authenticationRequestBuilder;
146+
}
147+
148+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package {{invokerPackage}}.auth;
2+
3+
public enum OauthFlow {
4+
accessCode, implicit, password, application
5+
}

0 commit comments

Comments
 (0)