Skip to content

Commit 1e4238b

Browse files
Daniel Tyreuss-gromov
authored andcommitted
support OAuth 2 refresh token (+update urls for Google)
1 parent 23e7f93 commit 1e4238b

10 files changed

Lines changed: 76 additions & 23 deletions

File tree

changelog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Support response in gzip.
44
* differentiate OAuth1 Access token, OAuth 1 Request Token and OAuth 2 Access token, make them conforms RFCs
55
* OAuth 1 APIs can choose whether to pass empty oauth_token param in requests
6+
* Support refresh tokens for OAuth2 (very thanks to P. Daniel Tyreus https://github.com/pdtyreus)
67

78
[2.2.2]
89
* make all APIs to be extentable (have protected constructors, useful for testing)

scribejava-apis/src/main/java/com/github/scribejava/apis/FacebookApi.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,8 @@
99
import com.github.scribejava.core.utils.OAuthEncoder;
1010
import com.github.scribejava.core.utils.Preconditions;
1111

12-
/***
12+
/**
1313
* Facebook v2.5 API
14-
*
1514
*/
1615
public class FacebookApi extends DefaultApi20 {
1716

@@ -22,6 +21,7 @@ protected FacebookApi() {
2221
}
2322

2423
private static class InstanceHolder {
24+
2525
private static final FacebookApi INSTANCE = new FacebookApi();
2626
}
2727

scribejava-apis/src/main/java/com/github/scribejava/apis/GoogleApi20.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public Verb getAccessTokenVerb() {
3434

3535
@Override
3636
public String getAccessTokenEndpoint() {
37-
return "https://accounts.google.com/o/oauth2/token";
37+
return "https://www.googleapis.com/oauth2/v4/token";
3838
}
3939

4040
@Override

scribejava-apis/src/main/java/com/github/scribejava/apis/NeteaseWeibooApi.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,15 @@ public String getAccessTokenEndpoint() {
3232
return ACCESS_TOKEN_URL;
3333
}
3434

35-
@Override
3635
/**
3736
* this method will ignore your callback if you're creating a desktop client please choose this url else your can
3837
* call getAuthenticateUrl
3938
*
4039
* via
4140
* http://open.t.163.com/wiki/index.php?title=%E8%AF%B7%E6%B1%82%E7%94%A8%E6%88%B7%E6%8E%88%E6%9D%83Token(oauth/authorize)
41+
* @return url to redirect user to (to get code)
4242
*/
43+
@Override
4344
public String getAuthorizationUrl(OAuth1RequestToken requestToken) {
4445
return String.format(AUTHORIZE_URL, requestToken.getToken());
4546
}

scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FacebookExample.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public static void main(String... args) {
2727
.state(secretState)
2828
.callback("http://www.example.com/oauth_callback/")
2929
.build(FacebookApi.instance());
30+
3031
final Scanner in = new Scanner(System.in, "UTF-8");
3132

3233
System.out.println("=== " + NETWORK_NAME + "'s OAuth Workflow ===");

scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20Example.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import com.github.scribejava.core.model.Verb;
1111
import com.github.scribejava.core.model.Verifier;
1212
import com.github.scribejava.core.oauth.OAuth20Service;
13+
import java.util.HashMap;
14+
import java.util.Map;
1315

1416
public abstract class Google20Example {
1517

@@ -35,7 +37,13 @@ public static void main(String... args) {
3537

3638
// Obtain the Authorization URL
3739
System.out.println("Fetching the Authorization URL...");
38-
final String authorizationUrl = service.getAuthorizationUrl();
40+
//pass access_type=offline to get refresh token
41+
//https://developers.google.com/identity/protocols/OAuth2WebServer#preparing-to-start-the-oauth-20-flow
42+
final Map<String, String> additionalParams = new HashMap<>();
43+
additionalParams.put("access_type", "offline");
44+
//force to reget refresh token (if usera are asked not the first time)
45+
additionalParams.put("prompt", "consent");
46+
final String authorizationUrl = service.getAuthorizationUrl(additionalParams);
3947
System.out.println("Got the Authorization URL!");
4048
System.out.println("Now go and authorize ScribeJava here:");
4149
System.out.println(authorizationUrl);
@@ -58,8 +66,14 @@ public static void main(String... args) {
5866

5967
// Trade the Request Token and Verfier for the Access Token
6068
System.out.println("Trading the Request Token for an Access Token...");
61-
final OAuth2AccessToken accessToken = service.getAccessToken(verifier);
69+
OAuth2AccessToken accessToken = service.getAccessToken(verifier);
6270
System.out.println("Got the Access Token!");
71+
System.out.println("(if your curious it looks like this: " + accessToken
72+
+ ", 'rawResponse'='" + accessToken.getRawResponse() + "')");
73+
74+
System.out.println("Refreshing the Access Token...");
75+
accessToken = service.refreshAccessToken(accessToken.getRefreshToken());
76+
System.out.println("Refreshed the Access Token!");
6377
System.out.println("(if your curious it looks like this: " + accessToken
6478
+ ", 'rawResponse'='" + accessToken.getRawResponse() + "')");
6579
System.out.println();

scribejava-core/src/main/java/com/github/scribejava/core/model/OAuth2AccessToken.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44
import java.util.Objects;
55

66
/**
7-
* Represents an OAuth 2 Access Token http://tools.ietf.org/html/rfc6749#section-5.1
7+
* Represents an OAuth 2 Access token.
8+
* <p>
9+
* http://tools.ietf.org/html/rfc6749#section-5.1
10+
*
11+
* @see <a href="https://tools.ietf.org/html/rfc6749#section-4.1.4">OAuth 2 Access Token Specification</a></p>
812
*/
913
public class OAuth2AccessToken extends Token {
1014

scribejava-core/src/main/java/com/github/scribejava/core/model/OAuthConstants.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ public interface OAuthConstants {
2020
String OUT_OF_BAND = "oob";
2121
String VERIFIER = "oauth_verifier";
2222
String HEADER = "Authorization";
23-
OAuth1RequestToken EMPTY_TOKEN = new OAuth1RequestToken("", "");
2423
String SCOPE = "scope";
2524

2625
// OAuth 2.0
@@ -29,6 +28,7 @@ public interface OAuthConstants {
2928
String CLIENT_SECRET = "client_secret";
3029
String REDIRECT_URI = "redirect_uri";
3130
String CODE = "code";
31+
String REFRESH_TOKEN = "refresh_token";
3232
String GRANT_TYPE = "grant_type";
3333
String AUTHORIZATION_CODE = "authorization_code";
3434
String STATE = "state";

scribejava-core/src/main/java/com/github/scribejava/core/oauth/OAuth10aService.java

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import com.github.scribejava.core.model.AbstractRequest;
99
import com.github.scribejava.core.model.OAuth1AccessToken;
1010
import com.github.scribejava.core.model.OAuth1RequestToken;
11-
import com.github.scribejava.core.model.OAuth1Token;
1211
import com.github.scribejava.core.model.OAuthAsyncRequestCallback;
1312
import com.github.scribejava.core.model.OAuthConfig;
1413
import com.github.scribejava.core.model.OAuthConstants;
@@ -50,7 +49,7 @@ public OAuth1RequestToken getRequestToken() {
5049

5150
config.log("setting oauth_callback to " + config.getCallback());
5251
request.addOAuthParameter(OAuthConstants.CALLBACK, config.getCallback());
53-
addOAuthParams(request, OAuthConstants.EMPTY_TOKEN);
52+
addOAuthParams(request, "");
5453
appendSignature(request);
5554

5655
config.log("sending request...");
@@ -62,7 +61,7 @@ public OAuth1RequestToken getRequestToken() {
6261
return api.getRequestTokenExtractor().extract(body);
6362
}
6463

65-
private void addOAuthParams(AbstractRequest request, OAuth1Token token) {
64+
private void addOAuthParams(AbstractRequest request, String tokenSecret) {
6665
final OAuthConfig config = getConfig();
6766
request.addOAuthParameter(OAuthConstants.TIMESTAMP, api.getTimestampService().getTimestampInSeconds());
6867
request.addOAuthParameter(OAuthConstants.NONCE, api.getTimestampService().getNonce());
@@ -72,7 +71,7 @@ private void addOAuthParams(AbstractRequest request, OAuth1Token token) {
7271
if (config.hasScope()) {
7372
request.addOAuthParameter(OAuthConstants.SCOPE, config.getScope());
7473
}
75-
request.addOAuthParameter(OAuthConstants.SIGNATURE, getSignature(request, token));
74+
request.addOAuthParameter(OAuthConstants.SIGNATURE, getSignature(request, tokenSecret));
7675

7776
config.log("appended additional OAuth parameters: " + MapUtils.toString(request.getOauthParameters()));
7877
}
@@ -87,8 +86,8 @@ public final OAuth1AccessToken getAccessToken(OAuth1RequestToken requestToken, V
8786
}
8887

8988
/**
90-
* Start the request to retrieve the access token. The optionally provided callback will be called with the Token
91-
* when it is available.
89+
* Start the request to retrieve the access token. The optionally provided
90+
* callback will be called with the Token when it is available.
9291
*
9392
* @param requestToken request token (obtained previously or null)
9493
* @param verifier verifier code
@@ -122,7 +121,7 @@ protected void prepareAccessTokenRequest(AbstractRequest request, OAuth1RequestT
122121
request.addOAuthParameter(OAuthConstants.TOKEN, requestToken.getToken());
123122
request.addOAuthParameter(OAuthConstants.VERIFIER, verifier.getValue());
124123
config.log("setting token to: " + requestToken + " and verifier to: " + verifier);
125-
addOAuthParams(request, requestToken);
124+
addOAuthParams(request, requestToken.getTokenSecret());
126125
appendSignature(request);
127126
}
128127

@@ -134,20 +133,18 @@ public void signRequest(OAuth1AccessToken token, AbstractRequest request) {
134133
request.addOAuthParameter(OAuthConstants.TOKEN, token.getToken());
135134
}
136135
config.log("setting token to: " + token);
137-
addOAuthParams(request, token);
136+
addOAuthParams(request, token.getTokenSecret());
138137
appendSignature(request);
139138
}
140139

141-
/**
142-
* {@inheritDoc}
143-
*/
144140
@Override
145141
public String getVersion() {
146142
return VERSION;
147143
}
148144

149145
/**
150-
* Returns the URL where you should redirect your users to authenticate your application.
146+
* Returns the URL where you should redirect your users to authenticate your
147+
* application.
151148
*
152149
* @param requestToken the request token you need to authorize
153150
* @return the URL where you should redirect your users
@@ -156,13 +153,12 @@ public String getAuthorizationUrl(OAuth1RequestToken requestToken) {
156153
return api.getAuthorizationUrl(requestToken);
157154
}
158155

159-
private String getSignature(AbstractRequest request, OAuth1Token token) {
156+
private String getSignature(AbstractRequest request, String tokenSecret) {
160157
final OAuthConfig config = getConfig();
161158
config.log("generating signature...");
162159
config.log("using base64 encoder: " + Base64Encoder.type());
163160
final String baseString = api.getBaseStringExtractor().extract(request);
164-
final String signature = api.getSignatureService()
165-
.getSignature(baseString, config.getApiSecret(), token.getTokenSecret());
161+
final String signature = api.getSignatureService().getSignature(baseString, config.getApiSecret(), tokenSecret);
166162

167163
config.log("base string is: " + baseString);
168164
config.log("signature is: " + signature);

scribejava-core/src/main/java/com/github/scribejava/core/oauth/OAuth20Service.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,42 @@ protected <T extends AbstractRequest> T createAccessTokenRequest(Verifier verifi
7878
return request;
7979
}
8080

81+
public final OAuth2AccessToken refreshAccessToken(String refreshToken) {
82+
final Response response = createRefreshTokenRequest(refreshToken,
83+
new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint(), this)).send();
84+
return api.getAccessTokenExtractor().extract(response.getBody());
85+
}
86+
87+
public final Future<OAuth2AccessToken> refreshAccessTokenAsync(String refreshToken,
88+
OAuthAsyncRequestCallback<OAuth2AccessToken> callback) {
89+
return refreshAccessTokenAsync(refreshToken, callback, null);
90+
}
91+
92+
public final Future<OAuth2AccessToken> refreshAccessTokenAsync(String refreshToken,
93+
OAuthAsyncRequestCallback<OAuth2AccessToken> callback, ProxyServer proxyServer) {
94+
final OAuthRequestAsync request = createRefreshTokenRequest(refreshToken,
95+
new OAuthRequestAsync(api.getAccessTokenVerb(), api.getAccessTokenEndpoint(), this));
96+
return request.sendAsync(callback, new OAuthRequestAsync.ResponseConverter<OAuth2AccessToken>() {
97+
@Override
98+
public OAuth2AccessToken convert(com.ning.http.client.Response response) throws IOException {
99+
return getApi().getAccessTokenExtractor()
100+
.extract(OAuthRequestAsync.RESPONSE_CONVERTER.convert(response).getBody());
101+
}
102+
}, proxyServer);
103+
}
104+
105+
protected <T extends AbstractRequest> T createRefreshTokenRequest(String refreshToken, T request) {
106+
if (refreshToken == null || refreshToken.isEmpty()) {
107+
throw new IllegalArgumentException("The refreshToken cannot be null or empty");
108+
}
109+
final OAuthConfig config = getConfig();
110+
request.addParameter(OAuthConstants.CLIENT_ID, config.getApiKey());
111+
request.addParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret());
112+
request.addParameter(OAuthConstants.REFRESH_TOKEN, refreshToken);
113+
request.addParameter(OAuthConstants.GRANT_TYPE, OAuthConstants.REFRESH_TOKEN);
114+
return request;
115+
}
116+
81117
/**
82118
* {@inheritDoc}
83119
*/

0 commit comments

Comments
 (0)