microsoft / azure-spring-boot

Spring Boot Starters for Azure services
MIT License
374 stars 460 forks source link

OUATH - Authorization code - Invalid Access token #803

Closed CatchSandeepVaid closed 3 years ago

CatchSandeepVaid commented 4 years ago

Environment

Summary

I have implemented OAUTH (grant type = authorization code). Authentication is happening and i am getting the authorization code. Based on this code, i want to get access token (& refresh token) so that i can call Microsoft graph api to get multiple users information. I am getting incorrect access token based on authorization code I debugged the code and found: Spring's DefaultAuthorizationCodeTokenResponseClient.getTokenResponse() get called and it executes the POST request as : URL = https://login.microsoftonline.com/{my-tenant-id}oauth2/token?{grant_type=[authorization_code], code=[<my_auth_code>], redirect_uri=[http://localhost:8080/login/oauth2/code/azure], client_id=[<myClientId>], client_secret=[<myClientSecret]}

NOTE: This [Link ](https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-protocols-oauth-code) says "resource" parameter required in one of either the authorization or token requests.

"resource" parameter is missing from the above request to get access token (using auth code) & hence i get access code which is not valid.

Using postman, i executed the same request and added "resource" parameter manually with value as "https://graph.microsoft.com". The access token which i now receive is valid and using this i am able to query the graph API.

saragluna commented 4 years ago

Thanks for reaching out. Could you please provide more information, such as which version do you use, how do you configure the web security? And what do you mean by implemented OAUTH?

CatchSandeepVaid commented 4 years ago

@saragluna - I am using the following versions: a) Spring Boot - 2.1.9.RELEASE b) azure-active-directory-spring-boot-starter - 2.2.1 (this gives me warning overriding managed version 2.1.7 for azure-active-directory-spring-boot-starter)

I have configured web security as:

@EnableWebSecurity (debug=true)
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 @Autowired
    private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService;
  @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable().authorizeRequests()
        .anyRequest().authenticated()
        .and()
        .oauth2Login()
        .userInfoEndpoint()
        .oidcUserService(oidcUserService);
}
}

In application.properties, i have specified the following properties:

azure.activedirectory.tenant-id=xxxxx
spring.security.oauth2.client.registration.azure.client-id=xxxx
azure.activedirectory.client-id=xxx
spring.security.oauth2.client.registration.azure.client-secret=xxx
azure.activedirectory.client-secret=xxx
azure.activedirectory.active-directory-groups=All Staff - Pune IN

spring.security.oauth2.client.registration.azure.provider=azure-oauth-provider
spring.security.oauth2.client.registration.azure.scope=openid, https://graph.microsoft.com/user.read
spring.security.oauth2.client.registration.azure.authorization-grant-type=authorization_code
spring.security.oauth2.client.provider.azure-oauth-provider.authorization-uri=https://login.microsoftonline.com/${azure.activedirectory.tenant-id}/oauth2/authorize
spring.security.oauth2.client.provider.azure-oauth-provider.token-uri=https://login.microsoftonline.com/${azure.activedirectory.tenant-id}/oauth2/token
spring.security.oauth2.client.provider.azure-oauth-provider.user-info-uri=https://login.microsoftonline.com/${azure.activedirectory.tenant-id}/openid/userinfo
spring.security.oauth2.client.provider.azure-oauth-provider.jwk-set-uri=https://login.microsoftonline.com/${azure.activedirectory.tenant-id}/discovery/keys

By implemented OAUTH, i mean, i am using out-of-box OAUTH feature of spring security & azure-active-directory-spring-boot-starter.

I have following queries as well: Q1: For getting access_token based on auth code, should it be done by Spring Security or azure-active-directory-spring-boot-starter? In my case, Spring security is doing this but it does not send "resource" parameter in this request as required by Microsoft AD Link and hence the access token which it receives in response is not a valid access token.

I also checked AADOAuth2UserService.loadUser() code from azure-spring-boot. It fetches access token based on idToken ( & not auth code) but in its request, it sends the "resource" parameter and hence it gets a valid access_token in response. NOTE: This "resource" param is added using AuthenticationContext.acquireTokenOnBehalfOf(...) adal4j jar file.

CatchSandeepVaid commented 4 years ago

The above microsoft link says "Resource parameter is required in one of either the authorization or token requests". I added this param in the authorization request like : spring.security.oauth2.client.provider.azure-oauth-provider.authorization-uri=https://login.microsoftonline.com/${azure.activedirectory.tenant-id}/oauth2/authorize?resource=https://graph.microsoft.com

and now i am getting a valid access token (through spring) which i can access as : `OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) SecurityContextHolder.getContext().getAuthentication();

    OAuth2AuthorizedClient oAuth2AuthorizedClient = this.authorizedClientService.loadAuthorizedClient(
                                                oauthToken.getAuthorizedClientRegistrationId(), oauthToken.getName());

String accessToken = oAuth2AuthorizedClient.getAccessToken().getTokenValue();`

Still i have the following queries: Q1: The access token is short lived. So after it expires, i want to get a new access token based on refresh_token. Does azure-active-directory-spring-boot-starter provides this service out-of-box Or i need to write my own code for this?

Q2: azure-active-directory-spring-boot-starter doc says that it also supports Microsoft identity platform(v2). Does it uses MSAL for this?

saragluna commented 4 years ago

Thanks for your updating. There are a few configurations that look not perfect for me.

For your questions:

CatchSandeepVaid commented 4 years ago

Thanks for your reply.

  1. resource - I think its required. The link says "It may also be an external resource like https://graph.microsoft.com. **This is required in one of either the authorization or token requests**". Moreover if we dont pass this parameter, we dont get valid access token.

  2. Regarding refreshing access token - It seems i have to write some code. I wrote the code and was able to get new access token (& new refresh token) based on access token but how can i set these new tokens in Spring Security context so that in further calls, i can get the access token to like:

    OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) SecurityContextHolder.getContext()
                .getAuthentication();
    OAuth2AuthorizedClient oAuth2AuthorizedClient = this.authorizedClientService
                .loadAuthorizedClient(oauthToken.getAuthorizedClientRegistrationId(), oauthToken.getName());
    String accessToken = oAuth2AuthorizedClient.getAccessToken().getTokenValue();

Code for getting accessToken (& new refresh token) using refresh token:

String refreshToken = oAuth2AuthorizedClient.getRefreshToken().getTokenValue(); 
         AuthenticationResult result = null;
            ExecutorService service = null;
            try {
                service = Executors.newFixedThreadPool(1);
                ServiceEndpoints serviceEndpoints = serviceEndpointsProps.getServiceEndpoints(aadAuthProps.getEnvironment());
                AuthenticationContext context=null;
                try {
                    context = new AuthenticationContext(serviceEndpoints.getAadSigninUri() + tenantId + "/", true, Executors.newSingleThreadExecutor());
                } catch (MalformedURLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                //context.setCorrelationId(getCorrelationId());
                ClientCredential clientCredential = new ClientCredential(clientId,clientSecret);
                final Future<AuthenticationResult> future = context.acquireTokenByRefreshToken(refreshToken,clientCredential,null);
                try {
                    result = future.get();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            } finally {
                if (service != null) {
                    service.shutdown();
                }
            }
System.out.println(result.getAccessToken());

Is there a better way to get access_token from spring security?

  1. I am facing another issue related to CORS. Scenario is: a) User hits my app root url, spring detects user is not authenticated and redirects it to microsoft login url to get auth code and the entire flow works fine and my app UI gets rendered to user.

    b) I restart my server. User clicks some button on UI which executes an AJAX call to my server. Spring identifies it as un-authenticated request and hence redirects it to microsoft login url. but This time i see additional request header (origin: localhost:8080) added to this request which caused the CORS issue. How to resolve this issue?

NOTE: Requests to microsoft login page in a) & b) are identical except in b) extra origin request header is added which causes CORS issue.

jacko9et commented 4 years ago

Any update on this issue?

chenrujun commented 3 years ago

Closing this issue. Because it's not active for a long time. If anyone have similar issue, please create issue in new repo: https://github.com/Azure/azure-sdk-for-java/issues