OAuth2 v2.0 authorize - error AADSTS90009 Application is requesting a token for itself - oauth-2.0

Requirement:
Use OAuth2 Implicit Grant flow do authorization in swagger ui page.
Swagger configuration code:
#Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("*"))
.paths(PathSelectors.any())
.build()
.securitySchemes(Collections.singletonList(securitySchema()))
.securityContexts(Collections.singletonList(securityContext()));
}
private SecurityScheme securitySchema() {
String scopeForAppIdUri = "http://xxxxxxx/.default";
AuthorizationScope authorizationScopeForAppIdUri = new AuthorizationScope(scopeForAppIdUri, "scope to restrict access to data protected by APIs");
List<AuthorizationScope> authorizationScopes = Collections.singletonList(authorizationScopeForAppIdUri);
LoginEndpoint loginEndpoint = new LoginEndpoint("https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/authorize");
List<GrantType> grantTypes = Collections.singletonList(new ImplicitGrant(loginEndpoint, "Authorization"));
return new OAuth("oauth2", authorizationScopes, grantTypes, null);
}
private SecurityContext securityContext() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
List<SecurityReference> securityReferences = Collections.singletonList(
new SecurityReference("oauth2", new AuthorizationScope[]{authorizationScope}));
return SecurityContext.builder().securityReferences(securityReferences).build();
}
Sent request:
https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/authorize
?response_type=token
&client_id={client id GUID}
&redirect_uri=http://localhost:8080/webjars/springfox-swagger-ui/oauth2-redirect.html
&scope=https://xxxxxxx/.default
&state=xxxxxxxxx
Error:
AADSTS90009: Application is requesting a token for itself. This scenario is supported only if resource is specified using the GUID based App identifier
Question:
There's a similar question about this error: OAuth 2.0 and Azure Active Directory - error AADSTS90009, which suggests to set 'resource' parameter to be client id for v1.0, so for v2.0, it can clear the error by setting 'scope' value to be client id.
But for my case, Our endpoints will validate 'aud' value in token that should equals to the application id uri 'http://xxxxxxx'(this feature cannot change), which means i cannot just set 'scope' to be '{client-id}/.default', or else even though it can return token successfully but the token cannot pass the validation of endpoint.

Related

How to set modify the access token request entity for Client Credentials grant when using Spring Security OAuth2 framrwork

I'm writing client for a 3rd party service that doesn't have the standard request format for getting an access token. The access token request body is a JSON with two attributes and the client_id and client_secret needs to be sent as a basic auth header. How do I build the custom request entity and headers converter to appropriately set these values in the access token request?
I have the client configuration with the client manager and responseclient.
public class RestClientConfig {
private final ClientRegistrationRepository clientRegistrationRepository;
private final OAuth2AuthorizedClientRepository authorizedClientRepository;
#Bean
public OAuth2AuthorizedClientManager authorizedClientManager(OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient){
OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials(configurer -> configurer.accessTokenResponseClient(accessTokenResponseClient))
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
#Bean
public OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient(){
OAuth2ClientCredentialsGrantRequestEntityConverter requestEntityConverter =
new OAuth2ClientCredentialsGrantRequestEntityConverter();
requestEntityConverter.setParametersConverter(null); --> this is where I'm stuck. Need to build a request entity converter bean to pass to this method
DefaultClientCredentialsTokenResponseClient accessTokenResponseClient =
new DefaultClientCredentialsTokenResponseClient();
accessTokenResponseClient.setRequestEntityConverter(requestEntityConverter);
return accessTokenResponseClient;
}
}

Why is my GraphServiceClient reauthenticating at every API call?

I am using Microsoft Graph API to call some endpoints. I am using the SDK for C#.
When opening a fiddler trace, I found out that my _graphClientService is issuing an authentication to get a new token at every call. Why would that happen and how to prevent it?
It is also causing this error in some calls.
AADSTS50196: The server terminated an operation because it encountered a client request loop
It looks like this piece of code works. It generates a GraphServiceClient that reuses the same token at every call, instead of generating a new one.
public GraphServiceClient GenerateGraphUserClient()
{
string userToken = GetUserAccessToken();
GraphServiceClient client= new GraphServiceClient("https://graph.microsoft.com/v1.0", new DelegateAuthenticationProvider(async (requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", userToken);
}));
return client;
}
public string GetUserAccessToken()
{
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
IPublicClientApplication publicClientApplication = PublicClientApplicationBuilder
.Create(_clientId)
.WithTenantId(_domain)
.Build();
var securePassword = new SecureString();
foreach (char c in _password)
securePassword.AppendChar(c);
var result = publicClientApplication.AcquireTokenByUsernamePassword(scopes, _userName, securePassword).ExecuteAsync().Result;
return result.AccessToken;
}
If you are using MSAL.NET you can cache a token. Public client applications (desktop/mobile apps) should try to get a token from the cache before acquiring a token by another method.
Acquisition methods on confidential client applications manage the cache themselves.
Resource:
Token cache serialization in MSAL.NET

JHipster - OAuth2/OIDC need to read groups from access token

The JHipster OAuth2/OIDC default configuration expects the "groups' to be found in the idToken. Can anyone explain how to read the "groups" from the access token instead?
Here are the changes made to retrieve the user's groups / granted authorities from the access token.
Note that for my case the Access Token (JSON) that the auth code is exchanged for contains an "access_token" field as a peer to the idToken. The "access_token" field is an ID or reference to the actual access token with the user's groups. An extra http request is needed to retrieve that "actual" access token.
For Okta the access token is a JWT similar to the idToken so if for some reason you need to configure Okta to add the groups to the access token instead of the idToken you will find them there.
Solution was based in this Spring doc:
Delegation-based strategy with OAuth2UserService
In your WebSecurityConfigurerAdapter class edit your oauth2Login config:
.oauth2Login().userInfoEndpoint().oidcUserService(this.oidcUserService());
Then create the custom oidcUserService():
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
final OidcUserService delegate = new OidcUserService();
return (userRequest) -> {
// Delegate to the default implementation for loading a user
OidcUser oidcUser = delegate.loadUser(userRequest);
// The access token will be a reference to the actual token
// ( for Okta this would be the actual JWT access token )
String accessTokenRef = userRequest.getAccessToken().getTokenValue();
// Call the end point to get the actual access_token
// ( httpClient is just a RestTemplate impl w/the required configs )
String[] groups = httpClient.fetchGroups(accessTokenRef);
// Create the GrantedAuthority objs & add to mappedAuthorities set
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
for (String group: groups) {
mappedAuthorities.add(new SimpleGrantedAuthority(group));
}
// Create a copy of oidcUser but use the mappedAuthorities instead
oidcUser = new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo());
return oidcUser;
};
}
If you are using JHipster there will be a GrantedAuthoritiesMapper that will need to be updated to map the authorities passed in directly to your application roles rather than reading them from the idToken. Something like:
#Bean
public GrantedAuthoritiesMapper userAuthoritiesMapper() {
return (authorities) -> {
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
Collection<String> roles = new HashSet();
authorities.forEach(authority -> {
roles.add(authority.getAuthority());
});
List<GrantedAuthority> list = SecurityUtils.mapRolesToGrantedAuthorities(roles);
mappedAuthorities = new HashSet<GrantedAuthority>(list);
return mappedAuthorities;
};
}
There are likely some other ways to do this and I would be happy to hear any advice.
Thanks to the commenters for their help.

Spring OAuth2RestTemplate for accessing resource server gives 401 Unauthorized

I m trying to have an OAuth2Client using authorization_code grant type, I can authorize the user and redirect the url, but when I try to access the resource using OAuth2RestTemplate, I get 401 UnAuthorized
Is there something I need to do for the OAuth2RestTemplate to add the Authorization header ? I thought Spring-oauth2 will take care of adding the headers to OAuthRestTemplate by itself
Verified with TRACE logging as well
#GetMapping("/")
public OAuth2User hello(#AuthenticationPrincipal OAuth2User oAuth2User){
logger.info("User="+oAuth2User.getAttributes().get("unique_name"));
String response = oAuth2RestTemplate.getForObject("https://localhost:8090/me", String.class);
return oAuth2User;
}
#Bean
public OAuth2RestTemplate oauth2RestTemplate(OAuth2ClientContext oauth2ClientContext) {
return new OAuth2RestTemplate(azureDetails(),oauth2ClientContext);
}
#Bean
public AuthorizationCodeResourceDetails azureDetails() {
AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
details.setClientId("myclientId");
details.setClientSecret("myclientsecret");
details.setAccessTokenUri("https://login.microsoftonline.com/common/oauth2/token");
details.setUserAuthorizationUri("https://login.microsoftonline.com/common/oauth2/authorize");
details.setScope(Arrays.asList("openid","profile","User.Read","Calendars.Read","Chat.Read","Files.Read","Mail.Read","Notes.Read","Tasks.Read"));
return details;
}
OAuth2RestTemplate should do a GET on MS Graph API and get the response
You need to update your AccessTokenUri and UserAuthorizationUri, your AccessTokenUri should be https://login.microsoftonline.com/common/oauth2/v2.0/tokenand your UserAuthorizationUri should be https://login.microsoftonline.com/common/oauth2/v2.0/authorize. For more details, please refer to https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow.

How to acquire a new Access token using Refresh token in Google OAuth 2.0 in .NET?

I have built MVC app using OAuth 2.0 to Access Google APIs.
On thirst call I receive an Access token + Refresh token.
Next calls come without a Refresh token, its ok, I saved it on a first call.
After 1 hour Access token expires and I need to get a new one, using previously saved refresh token.
How do I check that Access token expired? Didnt see any IsExpired properties.
What is the proper syntax to acquire a new Access token using Refresh token (for MVC app)? Couldnt find any reference or documentation how to do that.
Should I write any new code or call existing API to do that?
Where should I do that, in my HomeController's Index action or before calling any Google API?
My app is built as described in here (basically the same code), but no code to acquire a new Access token: https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#web-applications-aspnet-mvc
Thank you
For more details I added here how I wrote the code.
HomeController:
public async Task<ActionResult> Index(CancellationToken cancellationToken)
{
if (result == null || result.Credential == null)
{
result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).AuthorizeAsync(cancellationToken);
if (result.Credential == null) return new RedirectResult(result.RedirectUri);
if (!string.IsNullOrEmpty(result.Credential.Token.RefreshToken))
{
SaveRefreshTocken(result.Credential.Token.RefreshToken);
}
}
return View();
}
SaveRefreshTocken - just saves a Refresh token in web.config.
public ActionResult Gmail()
{
if (result == null || result.Credential == null) throw new Exception("expired_credential");
return PartialView(GmailManager.GetGmail(result.Credential));
}
And, simplified GmailManager class:
public static class GmailManager
{
public static List<Message> GetGmail(UserCredential credential)
{
var mygmail = new MyGmail();
var service = new GmailService(new BaseClientService.Initializer { HttpClientInitializer = credential });
var request = service.Users.Messages.List("me");
request.Q = "in:inbox is:unread";
var messages = request.Execute().Messages;
return messages;
}
}
Question - where and how should I USE refresh token?
If I saved it, I would have to use it when Access token expires to get a new Access token, right?
However it doesnt seem like its trying to acquire a new access token automatically:

Resources