Skip to content

Commit 705ccb3

Browse files
committed
Merge branch 'resource-owner-password-credentials-grant' of https://github.com/maksimlikharev/scribejava into maksimlikharev-resource-owner-password-credentials-grant
2 parents 1d9553f + f0c92a3 commit 705ccb3

File tree

7 files changed

+330
-20
lines changed

7 files changed

+330
-20
lines changed

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@
8080
<version>4.12</version>
8181
<scope>test</scope>
8282
</dependency>
83+
<dependency>
84+
<groupId>com.google.code.gson</groupId>
85+
<artifactId>gson</artifactId>
86+
<version>2.6.2</version>
87+
<scope>test</scope>
88+
</dependency>
8389
<dependency>
8490
<groupId>commons-codec</groupId>
8591
<artifactId>commons-codec</artifactId>

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public interface OAuthConstants {
2121
String VERIFIER = "oauth_verifier";
2222
String HEADER = "Authorization";
2323
String SCOPE = "scope";
24+
String BASIC = "Basic";
2425

2526
// OAuth 2.0
2627
String ACCESS_TOKEN = "access_token";
@@ -32,4 +33,6 @@ public interface OAuthConstants {
3233
String GRANT_TYPE = "grant_type";
3334
String AUTHORIZATION_CODE = "authorization_code";
3435
String STATE = "state";
36+
String USERNAME = "username";
37+
String PASSWORD = "password";
3538
}

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

Lines changed: 93 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package com.github.scribejava.core.oauth;
22

3+
import com.github.scribejava.core.services.Base64Encoder;
34
import com.ning.http.client.ProxyServer;
45
import java.io.IOException;
6+
import java.nio.charset.Charset;
57
import java.util.concurrent.Future;
68
import com.github.scribejava.core.builder.api.DefaultApi20;
79
import com.github.scribejava.core.model.AbstractRequest;
@@ -30,10 +32,29 @@ public OAuth20Service(DefaultApi20 api, OAuthConfig config) {
3032
this.api = api;
3133
}
3234

35+
//sync version, protected to facilitate mocking
36+
protected OAuth2AccessToken sendTokenSync(OAuthRequest request) {
37+
return api.getAccessTokenExtractor().extract(request.send().getBody());
38+
}
39+
40+
//async version, protected to facilitate mocking
41+
protected Future<OAuth2AccessToken> sendTokenAsync(OAuthRequestAsync request,
42+
OAuthAsyncRequestCallback<OAuth2AccessToken> callback, ProxyServer proxyServer) {
43+
44+
return request.sendAsync(callback, new OAuthRequestAsync.ResponseConverter<OAuth2AccessToken>() {
45+
@Override
46+
public OAuth2AccessToken convert(com.ning.http.client.Response response) throws IOException {
47+
return getApi().getAccessTokenExtractor()
48+
.extract(OAuthRequestAsync.RESPONSE_CONVERTER.convert(response).getBody());
49+
}
50+
}, proxyServer);
51+
}
52+
3353
public final OAuth2AccessToken getAccessToken(String code) {
34-
final Response response = createAccessTokenRequest(code,
35-
new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint(), this)).send();
36-
return api.getAccessTokenExtractor().extract(response.getBody());
54+
final OAuthRequest request = createAccessTokenRequest(code,
55+
new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint(), this));
56+
57+
return sendTokenSync(request);
3758
}
3859

3960
/**
@@ -53,13 +74,8 @@ public final Future<OAuth2AccessToken> getAccessTokenAsync(String code,
5374
OAuthAsyncRequestCallback<OAuth2AccessToken> callback, ProxyServer proxyServer) {
5475
final OAuthRequestAsync request = createAccessTokenRequest(code,
5576
new OAuthRequestAsync(api.getAccessTokenVerb(), api.getAccessTokenEndpoint(), this));
56-
return request.sendAsync(callback, new OAuthRequestAsync.ResponseConverter<OAuth2AccessToken>() {
57-
@Override
58-
public OAuth2AccessToken convert(com.ning.http.client.Response response) throws IOException {
59-
return getApi().getAccessTokenExtractor()
60-
.extract(OAuthRequestAsync.RESPONSE_CONVERTER.convert(response).getBody());
61-
}
62-
}, proxyServer);
77+
78+
return sendTokenAsync(request, callback, proxyServer);
6379
}
6480

6581
protected <T extends AbstractRequest> T createAccessTokenRequest(String code, T request) {
@@ -78,9 +94,10 @@ protected <T extends AbstractRequest> T createAccessTokenRequest(String code, T
7894
}
7995

8096
public final OAuth2AccessToken refreshAccessToken(String refreshToken) {
81-
final Response response = createRefreshTokenRequest(refreshToken,
82-
new OAuthRequest(api.getAccessTokenVerb(), api.getRefreshTokenEndpoint(), this)).send();
83-
return api.getAccessTokenExtractor().extract(response.getBody());
97+
final OAuthRequest request = createRefreshTokenRequest(refreshToken,
98+
new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint(), this));
99+
100+
return sendTokenSync(request);
84101
}
85102

86103
public final Future<OAuth2AccessToken> refreshAccessTokenAsync(String refreshToken,
@@ -92,13 +109,8 @@ public final Future<OAuth2AccessToken> refreshAccessTokenAsync(String refreshTok
92109
OAuthAsyncRequestCallback<OAuth2AccessToken> callback, ProxyServer proxyServer) {
93110
final OAuthRequestAsync request = createRefreshTokenRequest(refreshToken,
94111
new OAuthRequestAsync(api.getAccessTokenVerb(), api.getRefreshTokenEndpoint(), this));
95-
return request.sendAsync(callback, new OAuthRequestAsync.ResponseConverter<OAuth2AccessToken>() {
96-
@Override
97-
public OAuth2AccessToken convert(com.ning.http.client.Response response) throws IOException {
98-
return getApi().getAccessTokenExtractor()
99-
.extract(OAuthRequestAsync.RESPONSE_CONVERTER.convert(response).getBody());
100-
}
101-
}, proxyServer);
112+
113+
return sendTokenAsync(request, callback, proxyServer);
102114
}
103115

104116
protected <T extends AbstractRequest> T createRefreshTokenRequest(String refreshToken, T request) {
@@ -113,6 +125,67 @@ protected <T extends AbstractRequest> T createRefreshTokenRequest(String refresh
113125
return request;
114126
}
115127

128+
/**
129+
* Request Access Token Password Grant sync version
130+
*
131+
* @param uname User name
132+
* @param password User password
133+
* @return OAuth2AccessToken
134+
*/
135+
136+
public final OAuth2AccessToken getAccessTokenPasswordGrant(String uname, String password) {
137+
final OAuthRequest request = createAccessTokenPasswordGrantRequest(uname, password,
138+
new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint(), this));
139+
140+
return sendTokenSync(request);
141+
}
142+
143+
/**
144+
* Request Access Token Password Grant async version
145+
*
146+
* @param uname User name
147+
* @param password User password
148+
* @param callback Optional callback
149+
* @return Future
150+
*/
151+
public final Future<OAuth2AccessToken> getAccessTokenPasswordGrantAsync(String uname, String password,
152+
OAuthAsyncRequestCallback<OAuth2AccessToken> callback) {
153+
final OAuthRequestAsync request = createAccessTokenPasswordGrantRequest(uname, password,
154+
new OAuthRequestAsync(api.getAccessTokenVerb(), api.getAccessTokenEndpoint(), this));
155+
156+
return getAccessTokenPasswordGrantAsync(uname, password, callback, null);
157+
}
158+
159+
public final Future<OAuth2AccessToken> getAccessTokenPasswordGrantAsync(String uname, String password,
160+
OAuthAsyncRequestCallback<OAuth2AccessToken> callback, ProxyServer proxyServer) {
161+
final OAuthRequestAsync request = createAccessTokenPasswordGrantRequest(uname, password,
162+
new OAuthRequestAsync(api.getAccessTokenVerb(), api.getAccessTokenEndpoint(), this));
163+
164+
return sendTokenAsync(request, callback, proxyServer);
165+
}
166+
167+
protected <T extends AbstractRequest> T createAccessTokenPasswordGrantRequest(String username, String password, T request) {
168+
final OAuthConfig config = getConfig();
169+
request.addParameter(OAuthConstants.USERNAME, username);
170+
request.addParameter(OAuthConstants.PASSWORD, password);
171+
172+
if (config.hasScope()) {
173+
request.addParameter(OAuthConstants.SCOPE, config.getScope());
174+
}
175+
176+
request.addParameter(OAuthConstants.GRANT_TYPE, OAuthConstants.PASSWORD);
177+
178+
request.addHeader(OAuthConstants.HEADER, OAuthConstants.BASIC + " " +
179+
Base64Encoder.getInstance().encode(
180+
String.format("%s:%s", config.getApiKey(), config.getApiSecret()).getBytes(
181+
Charset.forName("UTF-8")
182+
)
183+
)
184+
);
185+
186+
return request;
187+
}
188+
116189
/**
117190
* {@inheritDoc}
118191
*/
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.github.scribejava.core.oauth;
2+
3+
import java.util.concurrent.ExecutionException;
4+
import java.util.concurrent.Future;
5+
import java.util.concurrent.TimeUnit;
6+
import java.util.concurrent.TimeoutException;
7+
8+
/**
9+
*/
10+
class CompletedFuture<V> implements Future<V> {
11+
private final V result;
12+
13+
public CompletedFuture( V result ) {
14+
this.result = result;
15+
}
16+
17+
@Override
18+
public boolean cancel(boolean mayInterruptIfRunning) {
19+
return false;
20+
}
21+
22+
@Override
23+
public boolean isCancelled() {
24+
return false;
25+
}
26+
27+
@Override
28+
public boolean isDone() {
29+
return true;
30+
}
31+
32+
@Override
33+
public V get() throws InterruptedException, ExecutionException {
34+
return result;
35+
}
36+
37+
@Override
38+
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException,
39+
TimeoutException {
40+
41+
return result;
42+
}
43+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.github.scribejava.core.oauth;
2+
3+
import com.github.scribejava.core.builder.api.DefaultApi20;
4+
import com.github.scribejava.core.model.OAuthConfig;
5+
6+
/**
7+
*/
8+
class OAuth20ApiUnit extends DefaultApi20 {
9+
@Override
10+
public String getAccessTokenEndpoint() {
11+
return "http://localhost:8080/token";
12+
}
13+
14+
@Override
15+
public String getAuthorizationUrl(OAuthConfig config) {
16+
return "http://localhost:8080/authorize";
17+
}
18+
19+
public OAuth20Service createService(OAuthConfig config) {
20+
return new OAuth20ServiceUnit(this, config);
21+
}
22+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package com.github.scribejava.core.oauth;
2+
3+
import com.github.scribejava.core.builder.ServiceBuilder;
4+
import com.github.scribejava.core.model.OAuth2AccessToken;
5+
import com.github.scribejava.core.model.OAuthConstants;
6+
import com.github.scribejava.core.services.Base64Encoder;
7+
import com.google.gson.Gson;
8+
import com.google.gson.reflect.TypeToken;
9+
import org.junit.Assert;
10+
import org.junit.Test;
11+
12+
import java.nio.charset.Charset;
13+
import java.util.Map;
14+
import java.util.concurrent.ExecutionException;
15+
16+
/**
17+
*/
18+
public class OAuth20ServiceTest {
19+
20+
@Test
21+
public void shouldProduceCorrectRequestSync() {
22+
final OAuth20Service service = new ServiceBuilder()
23+
.apiKey("your_api_key")
24+
.apiSecret("your_api_secret")
25+
.build( new OAuth20ApiUnit() );
26+
27+
final OAuth2AccessToken token = service.getAccessTokenPasswordGrant("user1", "password1");
28+
final Gson json = new Gson();
29+
30+
Assert.assertNotNull(token);
31+
32+
Map<String, String> map = json.fromJson(token.getRawResponse(),
33+
new TypeToken<Map<String, String>>(){}.getType());
34+
35+
Assert.assertEquals(OAuth20ServiceUnit.token, map.get(OAuthConstants.ACCESS_TOKEN));
36+
Assert.assertEquals(OAuth20ServiceUnit.state, map.get(OAuthConstants.STATE));
37+
Assert.assertEquals(OAuth20ServiceUnit.expires, map.get("expires_in"));
38+
39+
final String authorize = Base64Encoder.getInstance().encode(
40+
String.format("%s:%s", service.getConfig().getApiKey(), service.getConfig().getApiSecret()).getBytes(
41+
Charset.forName("UTF-8")
42+
)
43+
);
44+
45+
Assert.assertEquals(OAuthConstants.BASIC + " " + authorize, map.get(OAuthConstants.HEADER));
46+
47+
Assert.assertEquals("user1", map.get("query-username"));
48+
Assert.assertEquals("password1", map.get("query-password"));
49+
Assert.assertEquals("password", map.get("query-grant_type"));
50+
}
51+
52+
@Test
53+
public void shouldProduceCorrectRequestAsync() throws ExecutionException, InterruptedException {
54+
final OAuth20Service service = new ServiceBuilder()
55+
.apiKey("your_api_key")
56+
.apiSecret("your_api_secret")
57+
.build( new OAuth20ApiUnit() );
58+
59+
final OAuth2AccessToken token = service.getAccessTokenPasswordGrantAsync("user1", "password1", null).get();
60+
final Gson json = new Gson();
61+
62+
Assert.assertNotNull(token);
63+
64+
Map<String, String> map = json.fromJson(token.getRawResponse(),
65+
new TypeToken<Map<String, String>>(){}.getType());
66+
67+
Assert.assertEquals(OAuth20ServiceUnit.token, map.get(OAuthConstants.ACCESS_TOKEN));
68+
Assert.assertEquals(OAuth20ServiceUnit.state, map.get(OAuthConstants.STATE));
69+
Assert.assertEquals(OAuth20ServiceUnit.expires, map.get("expires_in"));
70+
71+
final String authorize = Base64Encoder.getInstance().encode(
72+
String.format("%s:%s", service.getConfig().getApiKey(), service.getConfig().getApiSecret()).getBytes(
73+
Charset.forName("UTF-8")
74+
)
75+
);
76+
77+
Assert.assertEquals(OAuthConstants.BASIC + " " + authorize, map.get(OAuthConstants.HEADER));
78+
79+
Assert.assertEquals("user1", map.get("query-username"));
80+
Assert.assertEquals("password1", map.get("query-password"));
81+
Assert.assertEquals("password", map.get("query-grant_type"));
82+
}
83+
84+
}

0 commit comments

Comments
 (0)