|
1 | 1 | packagecom.github.scribejava.core.oauth;
|
2 | 2 |
|
3 |
| -importjava.io.IOException; |
4 |
| -importjava.io.OutputStream; |
5 |
| -importjava.util.concurrent.Future; |
| 3 | +importcom.fasterxml.jackson.databind.JsonNode; |
| 4 | +importcom.fasterxml.jackson.databind.ObjectMapper; |
6 | 5 | importcom.github.scribejava.core.builder.api.DefaultApi20;
|
7 | 6 | importcom.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor;
|
8 | 7 | importcom.github.scribejava.core.httpclient.HttpClient;
|
9 | 8 | importcom.github.scribejava.core.httpclient.HttpClientConfig;
|
| 9 | +importcom.github.scribejava.core.model.DeviceCode; |
10 | 10 | importcom.github.scribejava.core.model.OAuth2AccessToken;
|
| 11 | +importcom.github.scribejava.core.model.OAuth2AccessTokenErrorResponse; |
11 | 12 | importcom.github.scribejava.core.model.OAuth2Authorization;
|
12 | 13 | importcom.github.scribejava.core.model.OAuthAsyncRequestCallback;
|
13 | 14 | importcom.github.scribejava.core.model.OAuthConstants;
|
14 | 15 | importcom.github.scribejava.core.model.OAuthRequest;
|
15 | 16 | importcom.github.scribejava.core.model.Response;
|
16 | 17 | importcom.github.scribejava.core.model.Verb;
|
| 18 | +importcom.github.scribejava.core.oauth2.OAuth2Error; |
17 | 19 | importcom.github.scribejava.core.pkce.PKCE;
|
18 |
| -importjava.util.Map; |
19 |
| -importjava.util.concurrent.ExecutionException; |
20 | 20 | importcom.github.scribejava.core.revoke.TokenTypeHint;
|
| 21 | +importjava.io.IOException; |
| 22 | +importjava.io.OutputStream; |
21 | 23 | importjava.io.UnsupportedEncodingException;
|
22 | 24 | importjava.net.URLDecoder;
|
| 25 | +importjava.util.Map; |
| 26 | +importjava.util.concurrent.ExecutionException; |
| 27 | +importjava.util.concurrent.Future; |
| 28 | + |
| 29 | +importstaticcom.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor.extractRequiredParameter; |
| 30 | +importstaticcom.github.scribejava.core.oauth2.OAuth2Error.AUTHORIZATION_PENDING; |
| 31 | +importstaticcom.github.scribejava.core.oauth2.OAuth2Error.SLOW_DOWN; |
| 32 | +importstaticjava.lang.Thread.sleep; |
23 | 33 |
|
24 | 34 | publicclassOAuth20ServiceextendsOAuthService {
|
| 35 | +protectedstaticfinalObjectMapperOBJECT_MAPPER =newObjectMapper(); |
25 | 36 |
|
26 | 37 | privatestaticfinalStringVERSION ="2.0";
|
27 | 38 | privatefinalDefaultApi20api;
|
@@ -490,4 +501,83 @@ public String getResponseType() {
|
490 | 501 | publicStringgetDefaultScope() {
|
491 | 502 | returndefaultScope;
|
492 | 503 | }
|
| 504 | + |
| 505 | +/** |
| 506 | + * Requests a device code from a server. |
| 507 | + * |
| 508 | + * @see <a href="https://tools.ietf.org/html/rfc8628#section-3">rfc8628</a> |
| 509 | + * @see <a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-device-code"> |
| 510 | + * azure v2-oauth2-device-code</a> |
| 511 | + */ |
| 512 | +publicDeviceCodegetDeviceCode()throwsInterruptedException,ExecutionException,IOException { |
| 513 | +finalOAuthRequestrequest =newOAuthRequest(Verb.POST,api.getDeviceAuthorizationUrl()); |
| 514 | +request.addBodyParameter(OAuthConstants.CLIENT_ID,getApiKey()); |
| 515 | +request.addBodyParameter(OAuthConstants.SCOPE,getDefaultScope()); |
| 516 | +try (Responseresponse =execute(request)) { |
| 517 | +finalStringbody =response.getBody(); |
| 518 | +if (response.getCode() ==200) { |
| 519 | +finalJsonNoden =OBJECT_MAPPER.readTree(body); |
| 520 | +returnnewDeviceCode( |
| 521 | +extractRequiredParameter(n,"device_code",body).textValue(), |
| 522 | +extractRequiredParameter(n,"user_code",body).textValue(), |
| 523 | +extractRequiredParameter(n,"verification_uri",body).textValue(), |
| 524 | +n.path("interval").asInt(5), |
| 525 | +extractRequiredParameter(n,"expires_in",body).intValue()); |
| 526 | + }else { |
| 527 | +OAuth2AccessTokenJsonExtractor.instance().generateError(body); |
| 528 | +thrownewIllegalStateException();// generateError() always throws an exception |
| 529 | + } |
| 530 | + } |
| 531 | + } |
| 532 | + |
| 533 | +/** |
| 534 | + * Attempts to get a token from a server. |
| 535 | + * Function {@link #pollDeviceAccessToken(DeviceCode)} is usually used instead of this. |
| 536 | + * |
| 537 | + * @return token |
| 538 | + * @throws OAuth2AccessTokenErrorResponse |
| 539 | + * If {@link OAuth2AccessTokenErrorResponse#getError()} is |
| 540 | + * {@link OAuth2Error#AUTHORIZATION_PENDING} or {@link OAuth2Error#SLOW_DOWN}, |
| 541 | + * another attempt should be made after a while. |
| 542 | + * |
| 543 | + * @see #getDeviceCode() |
| 544 | + */ |
| 545 | +publicOAuth2AccessTokengetAccessTokenDeviceCodeGrant(DeviceCodedeviceCode) |
| 546 | +throwsIOException,InterruptedException,ExecutionException { |
| 547 | +finalOAuthRequestrequest =newOAuthRequest(Verb.POST,api.getAccessTokenEndpoint()); |
| 548 | +request.addParameter(OAuthConstants.GRANT_TYPE,"urn:ietf:params:oauth:grant-type:device_code"); |
| 549 | +request.addBodyParameter(OAuthConstants.CLIENT_ID,getApiKey()); |
| 550 | +request.addParameter("device_code",deviceCode.getDeviceCode()); |
| 551 | +try (Responseresponse =execute(request)) { |
| 552 | +returnapi.getAccessTokenExtractor().extract(response); |
| 553 | + } |
| 554 | + } |
| 555 | + |
| 556 | +/** |
| 557 | + * Periodically tries to get a token from a server (waiting for the user to give consent). |
| 558 | + * |
| 559 | + * @return token |
| 560 | + * @throws OAuth2AccessTokenErrorResponse |
| 561 | + * Indicates OAuth error. |
| 562 | + * |
| 563 | + * @see #getDeviceCode() |
| 564 | + */ |
| 565 | +publicOAuth2AccessTokenpollDeviceAccessToken(DeviceCodedeviceCode) |
| 566 | +throwsInterruptedException,ExecutionException,IOException { |
| 567 | +longintervalMillis =deviceCode.getIntervalSeconds() *1000; |
| 568 | +while (true) { |
| 569 | +try { |
| 570 | +returngetAccessTokenDeviceCodeGrant(deviceCode); |
| 571 | + }catch (OAuth2AccessTokenErrorResponsee) { |
| 572 | +if (e.getError() !=AUTHORIZATION_PENDING) { |
| 573 | +if (e.getError() ==SLOW_DOWN) { |
| 574 | +intervalMillis +=5000; |
| 575 | + }else { |
| 576 | +throwe; |
| 577 | + } |
| 578 | + } |
| 579 | + } |
| 580 | +sleep(intervalMillis); |
| 581 | + } |
| 582 | + } |
493 | 583 | }
|