First of all, let's see the spec Access Token Scope ,it show that :
The value of the scope parameter is expressed as a list of space-
delimited, case-sensitive strings. The strings are defined by the
authorization server.
What's the value of access token scope definded in Spring Security OAuth 2.0.3.RELEASE? Is Only "read","write" or "trust" etc? Can the scope Associated with resource server?
You can see github oauth api
Scopes are just strings so you can use any value you like ("read" "write" etc. are just examples in the samples). Their meaning is a contract between the Authorization Server and the Resource Servers.
Related
I'm requesting the following scopes (URL encoded):
offline_access user.read https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/SMTP.Send
The authorization process with OAuth 2.0 using the new Microsoft Graph API seems to work fine, but when using the access token to connect to IMAP over XOAuth2, I'm getting a NO AUTHENTICATE, which indicates the token is invalid.
It turns out this is not an issue with the user but rather with Microsoft's Graph API. Although it is not documented, you are currently not allowed to request a token with a scope that falls under two tenants, or it will choose one and fail silently.
In this case, User.Read falls under Microsoft Graph tenancy. Technically speaking, if your user is an organizational Outlook/Office365 user, they likely don't actually have Microsoft Graph installed and the correct scope would be https://outlook.office.com/User.Read. However the Outlook profile endpoint has been deprecated and would be unadvisable to use (you also have no way of knowing whether or not your user has MS Graph tenancy). It seems to resolve this, the user.read permission can be requested without specifying the Microsoft Graph URL.
This is essentially what you are doing above, but can be misleading as you are not actually requesting the general User.Read permission that can then be resolved to the Outlook tenant. What is actually happening is that User.Read permission is being mapped to some default tenant, and so your scopes actually contain multiple tenants (both the default tenant and Outlook).
Since this is not allowed, it fails silently and defaults to the default tenant. With most of their APIs this still works, but specifically with IMAP/SMTP you cannot request a larger scope/multi-tenant key or it will not validate over XOAuth2. You will note that the access tokens returned for IMAP/SMTP alone are always much smaller than the access tokens for other scopes.
To remedy this, you will need to request two access tokens. Firstly, you should use your authorization code to request a key within the following scope:
offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/SMTP.Send
Following this, you need to request an access token for the profile. However, as of October 2020, you are no longer allowed to use a single authorization code for multiple access token grants. So you will need to login the user once more -- the canonical way of doing this is to simply navigate them back to the auth URL, leaving the login_hint field empty. This will vary depending on how you are constructing your URL, but here is an example in JS:
url = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize?'
url += `client_id=${clientId}`
url += '&response_type=code'
url += '&redirect_uri=${redirectURI}'
url += '&response_mode=query'
url += '&login_hint='
url += '&scope=offline_access%20User.Read%20https%3A%2F%2Foutlook.office.com%2FIMAP.AccessAsUser.All%20https%3A%2F%2Foutlook.office.com%2FSMTP.Send'
url += '&state=12345
Note that your authorization code must request the full scope (including both User.Read and IMAP scopes like IMAP.AccessAsUser.All, for both access token requests. Specifying a smaller scope will not guarantee that the profile you read will necessarily correspond to the Outlook account.
After receiving this second authorization code (it won't ask the user to login manually again, just load a bit and automatically resolve a second code) you can request a new access token with the following scope:
user.read
You can include any other Graph API scopes above, but specifying something under Outlook and especially under IMAP will mix up your scopes. The response scope will still contain EAS access and Outlook scopes, but with the addition of the user.read permission.
You should use this second token to access the profile and refresh it separately from the first token (which should only be used for IMAP/SMTP).
So I currently have this POC that I'm tinkering right now. I was thinking if it was possible that I can implement a Spring oAuth2 with JWT with a Authorization Server and a Resource Server both in different projects?
Flow goes like this User gets a token or passes through the Authorization Server and as long as he has the token and it's not expired he can make requests on the resource server.
I think that is the usual way to implement that. You have one authorization service providing tokens, either itself is backed by a database containing user information or maybe is asking another user service if the credentials are valid. The returned tokens can be used to make authorized request against the resource service(s).
Maybe take a look at the grant flow here.
I am trying to get a custom web application to work with Azure B2C OAuth and the Spring OAuth2.0 framework.
The authentication leg comes back fine and I receive a JWT token. When the request for a token occurs afterwards I get the following error:
java.lang.IllegalStateException: Access token provider returned a null access token, which is illegal according to the contract.
at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:223) ~[spring-security-oauth2-2.0.8.RELEASE.jar:na]
at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173) ~[spring-security-oauth2-2.0.8.RELEASE.jar:na]
...
From some debugging of the spring code I can see the token is expected to be called access_token as seen in the OAuth2AccessToken class. From looking at the B2C tutorials their token is called token_id. Furthermore the applications.yml config I have for my spring application has a field called tokenName. Surely this should be used to pick up the token name field instead of the hardcoded static variable as above.
Am I missing something and is there a solution to my problem. Can I override the token name field used by the spring OAuth framework?
I'm going to go ahead and post this as the answer.
I started with the Spring tutorial, and made some modifications to it. For a working example, see the public github repo: https://github.com/Pytry/azure-b2c-oauth2.git
To properly parse the token received you will need:
A custom implementation of an AccessToken that will parse the JWT, pulling and setting variables as required by spring security. I extended DefaultOAuth2AccessToken and added this parsing to a private method called by the constructor.
If you are going to verify the RSA signature using the public keys, you will need a custom JWT object so you can access the header information. I chose to extend springs JWT, and add some parsing on creation to access the header. It may also be useful to have some custom Pojos for parsing the returned meta data and rsakey information into.
An extension of the JwtAccessTokenConverter, with an overridden "decode" method. Azure does not give a "user_name" nor a "client_id" in the returned id_token, so you need to add those. I also included some logic in the super class that I found suefull (such as converting strings to int/long when appropriate).
A custom UserDetailsManger to override the default in memory one. This can either retrioeve user information from the GraphAPI, or it can load it from your user repository. I actually did not create either of these, and instead used the default in memory service, but injected it into the token converter; then whenever a user was properly authenticated, I would add them to the managers store, or update them if they already existed.
There are a few things I have not done yet.
RSA verification is not being done. Any help on this is appreciated.
I am using Spring Security in my application.
I have all the pages secured. But couple of URL needs to be available both for system user and anonymous user.
But anonymous user should not have direct access to the URLs. He gets a link with unique token and gets access to some URLS if this token is valid.
What I want to do is:
In controller check if token in URL is valid
If it is - authenticate user in the system programmatically using some predefined login and password. This user will be configured to have authority to access necessary URLs.
The question is:
Is this a correct approach to perform user authentication programatically with some roles in controller if token is valid? Is this safe approach?
Security is an aspect. An aspect can be decoupled from your main code (controller) to reduce code duplication and improve flexibility. Move authentication code from controller to new filter (be sure that this filter executed after spring security filter chain). You will be able secure new URLs via web.xml (zero lines of code).
I think the better way to do this is:
move the shared operations into service layer
define a controller for those anonymous user and make its authority
as anonymous
check the validity of token in this controller
if valid, call some services method to perform the operations.
render the result in this controller
OAuth2ProtectedResourceFilter in org.springframework.security.oauth2.provider.filter:
Collection<String> resourceIds = auth.getClientAuthentication().getResourceIds();
if (resourceIds!=null && !resourceIds.isEmpty() && !resourceIds.contains(resourceId)) {
throw new InvalidTokenException("Invalid token does not contain resource id ("+resourceId+"): " + token);
}
I think it is not useful. What does this code check for?
Based on what I've gathered, it is the id of the resource service.
It becomes more clear when you consider separating your oauth token provider servlet and your resource servers for the purpose of api versioning. For example, say Client A (cA) has access to api1 and Client B (cB) has access to api2, you enforce this access by dictating in your resource server xml for api1 that its resource-id=api1 and then configure your client details for cA that they have resourceIds="api1", and likewise for [cB,api2].
This lets us protect api access and keep its protection declaration separate from, say, our client roles declaration.
It looks like it checks whether a client is authorized to view a particular resource. Not sure how the token variable is involved, it looks like there is some more relevant code that you have not shown.