Spring Oauth2 - multiple tokens per client id - spring-security-oauth2

We have implemented a server API using spring-oauth2. I have noticed that the server generates the same token per user/client id combination even when calling from separate devices. This causes an issue as my clients can run multiple instances: e.g. android and ios apps. I need a way to link the token to a specific instance and not re-use the same token.
An example where this is required is for GCM (or push notification) where the API needs to know which instance it is communicating with.
This is my current spring config:
<http pattern="/oauth/token" create-session="stateless"
authentication-manager-ref="clientAuthenticationManager"
entry-point-ref="oauthAuthenticationEntryPoint" xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<anonymous enabled="false" />
<http-basic entry-point-ref="oauthAuthenticationEntryPoint" />
<!-- include this only if you need to authenticate clients via request parameters -->
<custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
<oauth:authorization-server
client-details-service-ref="mongoclientDetails" token-services-ref="tokenServices"
user-approval-handler-ref="userApprovalHandler">
<!-- authorization-endpoint-url="/oauth/authorize" token-endpoint-url="/oauth/token"> -->
<oauth:authorization-code />
<oauth:implicit />
<oauth:refresh-token />
<oauth:client-credentials />
<oauth:password />
</oauth:authorization-server>
I prefer not to give each of the clients a different id as that would be it impractical. Any ideas?

So the DefaultAuthenticationKeyGeneration uses client_id, and scope to create a key and if that matches in the request to get token it serves up the previously generated token. So in your case, you could have ios, android, and the device id for scopes.
Here is my code.
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
.....
#Override
public void configure(ClientDetailsServiceConfigurer clients) {
clients.inMemory()
.withClient("my-trusted-client-with-secret")
.authorizedGrantTypes("client_credentials")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
//.scopes("read", "write", "trust")
.secret("somesecret")
.accessTokenValiditySeconds(3600);
}
}
Tests
» curl -H "Accept: application/json" my-trusted-client-with-secret:somesecret#localhost:8080/auth/oauth/token -d grant_type=client_credentials -d custid=1 -d siteid=2D -d scope="y"
{"access_token":"cust:site1:2D","token_type":"bearer","expires_in":3282,"scope":"y"}%
» curl -H "Accept: application/json" my-trusted-client-with-secret:somesecret#localhost:8080/auth/oauth/token -d grant_type=client_credentials -d custid=1 -d siteid=3D -d scope="z"
{"access_token":"cust:site1:3D","token_type":"bearer","expires_in":3290,"scope":"z"}%
» curl -H "Authorization: Bearer cust:site:3D" http://localhost:8080/dtn-auth/home
{"error":"invalid_token","error_description":"Invalid access token: cust:site:3D"}%
» curl -H "Authorization: Bearer cust:site1:3D" http://localhost:8080/dtn-auth/home
Hello World%
» curl -H "Authorization: Bearer cust:site1:2D" http://localhost:8080/dtn-auth/home
Hello World%
As you see I was able to generate multiple tokens for the same client_id and both of these tokens authenticated to access a resource from the resource server.

I think that you can take device ids in your request and generate token for each id or you can get flag that determine the type of device that call your api like ( Android, IOS) and generate token for each platform.

Related

Azure API - How do I setup CORS

I have REST API, that is hosted in Azure. If I make request in interactive console with GET method ('/api/pets'), request goes through just fine. But when I make POST request (POST '/api/pets'), CORS error appears.
Response in interacive console throws this error:
startup.cs
public void ConfigureServices(IServiceCollection services)
{
.
.
.
services.AddCors(options => options.AddPolicy("CorsPolicy",
builder =>
{
builder
.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
}));
.
.
.
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCors("CorsPolicy");
.
.
.
}
All API's CORS policy
<policies>
<inbound>
<cors>
<allowed-origins>
<origin>*</origin>
</allowed-origins>
<allowed-methods preflight-result-max-age="300">
<method>GET</method>
<method>POST</method>
<method>PUT</method>
<method>DELETE</method>
<method>HEAD</method>
<method>OPTIONS</method>
<method>PATCH</method>
<method>TRACE</method>
</allowed-methods>
<allowed-headers>
<header>*</header>
</allowed-headers>
<expose-headers>
<header>*</header>
</expose-headers>
</cors>
</inbound>
<backend>
<forward-request />
</backend>
<outbound />
<on-error />
</policies>
I also tried setting CORS policy on azure portal only in specific API, but it doesn't work
Everything works fine locally.
Can you try configure this using the portal?
Open your API in the Azure API Management section of the Azure portal
Select All operations, or a single operation
On the right, choose Inbound processing > Add policy
You will get a list of prefab policy templates. Choose the "CORS" one and configure it at will:
UPDATE:
Can you modify the XML as below
<cors>
<allowed-origins>
<origin>*</origin>
</allowed-origins>
<allowed-methods>
<method>*</method>
</allowed-methods>
<allowed-headers>
<header>*</header>
</allowed-headers>
<expose-headers>
<header>*</header>
</expose-headers>
</cors>
Few years ago I tried Outboud rules into API Managment Policies to add this headers to response:
<policies>
<inbound>
<base />
<cors allow-credentials="true">
...
</cors>
</inbound>
...
<outbound>
<base />
<set-header name="Access-Control-Allow-Origin" exists-action="override">
<value>#(context.Request.Headers.GetValueOrDefault("Origin",""))</value>
</set-header>
<set-header name="Access-Control-Allow-Credentials" exists-action="override">
<value>true</value>
</set-header>
</outbound>
...
</policies>
Also make sure that you are using app.UseCors() before response compression if exists and before app.UseEndpoints() or app.UseMvc()

HTTP Status 403 - Expected CSRF token not found.Has session expired?

I have a Spring MVC application, view layer is jsp based.At times I get this error message and this message is true, session really got expired.If I login once again then it is all fine.
I am using following mechanism to send CSRF token:
In head section 2 meta tags are added:
<meta name="_csrf" content="${_csrf.token}" />
<meta name="_csrf_header" content="${_csrf.headerName}" />
In every Ajax call,token and header are retrieved:
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
Then with XMLHttpRequest this header & token are sent:
$.ajax({
type : "GET",
url : xxx,
beforeSend : function(xhr) {
xhr.setRequestHeader(header, token);
},
complete : function() {
},
success : function(response) {
}
});
This is how it is in most jsp pages.I have tried to capture the token in an alert message & it works. I want to redirect the user to the login page if the session has expired via a page which will show why the user is getting redirected. How to go about this?
In the server side Spring we have used xml based configuraton:
<http auto-config="true" use-expressions="true">
...
<csrf/>
</http>
I added following to application-security.xml which solved the problem for this particular scenario, though I am facing CSRF exception in some other scenario :
<http auto-config="true" use-expressions="true">
...
<session-management invalid-session-url="/login">
<concurrency-control expired-url="/login" />
</session-management>
</http>

unable to exchange auth token with access token - redirect uri missmatch

I try to build below:
by following: this steps
however, i keep receiving redirect uri missmatch when i tried to exchange auth code (given by my mobile app) to google server - which i couldn't understand because technically there is no redirect uri required for my flow case...
here are the details:
in Android Client:
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestScopes(new Scope(Scopes.DRIVE_APPFOLDER))
.requestServerAuthCode(serverClientId, false)
.build();
/**** bla.... ****/
GoogleSignInAccount acct = result.getSignInAccount();
String authCode = acct.getServerAuthCode();
/**** android app will send this authCode to my server ****/
/**** sample authCode: 4/Jny2Mxxx3x09sy4pqY3ZAwSTEz8rw2xxxxC-4VxxxxM
in my backend server:
try:
# i receive authCode correctly from android app.
# and use authCode to exchange to Access Token to google server as below:
credentials = client.credentials_from_clientsecrets_and_code(
app.config.get('GG_APP_SECRET'),
['https://www.googleapis.com/auth/plus.me', 'profile', 'email'],
authCode)
except Exception as e:
log.info('>>>>> I always receive: redirect uri missmatch here: %s <<<<<', e)
return generate_response(code=400, error=False, type='Fail', message=str(e))
this is curl from my backend server:
curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' -d '{ \
"authCode": "4/HP_cP_t70pgBrxxx7sjzCil7kaUHkxxxerdkMxxxrRg" \
}' 'http://localhost:5005/api/user/register/gg'
this is my console settings:
Questions:
is the serverClientId in android client suppose to be the clientID of above image?
what is the redirect uri that i should put in google console above?
what should i set/configure for my redirect uri? or is there any specific settings that i need to do?
Ok I go it,
if you see this
you will found out:
def credentials_from_clientsecrets_and_code(filename, scope, code,
message=None,
redirect_uri='postmessage',
http=None,
cache=None,
device_uri=None):
and you realize that redirect_uri = 'postmessage' which in my case i dont have post message.
so what i do is to match that redirect_uri with authorize redirect uri that i have in google console
so for my case in my question above, i change my python code to:
credentials = client.credentials_from_clientsecrets_and_code(
app.config.get('GG_APP_SECRET'),
['https://www.googleapis.com/auth/plus.me', 'profile', 'email'],
authCode, redirect_uri='https://developers.google.com/oauthplayground')

User authentication failure when using Dojo and Spring Security due to failure to verify CSRF token

Below is a fragment of my Dojo code verifying the username and password of the user.
var deferred = dojo.xhrPost({
url : "../../j_spring_security_check",
load : function(response, ioArgs) {
return response;
},
handleAs : "json-comment-filtered",
timeout : 180000,
content : {
"j_username" : usrName,
"j_password" : password
},
error : function(response, ioArgs) {
return response;
}
});
On the server side Spring configuration is:
<form-login login-page="/service/authenticate" username-parameter="j_username" password-parameter="j_password" login-processing-url="/j_spring_security_check" default-target-url="/service/authenticate" always-use-default-target="true" authentication-failure-url="/service/authenticate" />
On attempting to authenticate the user. I keep getting:
Failed to load resource: the server responded with a status of 403 (Could not verify the provided CSRF token because your session was not found.)
on the xxx/j_spring_security_check endpoint.
I have enabled:
<headers><frame-options policy="SAMEORIGIN"/></headers>
The goal is to enable Cross Site Request Forgery (CSRF) protection. Any ideas on how I could solve this.
Two changes had to be made both on the server and client code base.
Server
This was clearly explained in the link shared. spring security reference
<http ...>
<csrf token-repository-ref="tokenRepository" />
</http>
<b:bean id="tokenRepository"
class="org.springframework.security.web.csrf.CookieCsrfTokenRepository">
<b:property name="cookieHttpOnly" value="false"/>
</b:bean>
Client
Intercepted xhr request and added the X-XSRF-TOKEN header by obtaining the XSRF-TOKEN from the cookie. This works but from a security perspective better alternatives than storing the token in cookie can be used.
require(
["dojo/request/notify", "dojo/cookie"],function(notify,Cookie ){
notify("send", function(response, cancel) {
response.xhr.setRequestHeader('X-XSRF-TOKEN', Cookie("XSRF-TOKEN"));
});}

How to add basic auth to a service callout policy

This is the service callout policy:
<ServiceCallout name="GeoCodeClient">
<Request clearPayload="false" variable="GeocodingRequest" />
<Response>GeocodingResponse</Response>
<Timeout>30000</Timeout>
<HTTPTargetConnection>
<URL>http://maps.googleapis.com/maps/api/geocode/json</URL>
</HTTPTargetConnection>
</ServiceCallout>
Let us say I have to access a resource that is username/password protected. How do I add that basic authorization to this policy to enable me to do that?
In our project a KeyValueMaps are used to store the basic auth info at org level. The authorisation information is retrieved using the KeyValueMap policy and added as the basic auth header to the request message.
See if this approach works for you.
To add Basic Authentication header for your service callout, you can use an 'AssignMessage' policy that sets the 'Authorization' header in the 'GeocodingRequest' as follows:
<AssignMessage enabled="true" continueOnError="true" async="false" name="AssignAuthorizationHeaderPolicy">
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
<AssignTo createNew="true" transport="http" type="request">GeocodingRequest</AssignTo>
<Add>
<Headers>
<Header name="Authorization">Basic YourAuthenticationHeader</Header>
</Headers>
</Add>
</AssignMessage>
Once you have created this policy, you will need to attach it in the request flow before the serviceCallout in the proxy.xml as flows:
<Step>
<FaultRules/>
<Name>AssignAuthorizationHeaderPolicy</Name>
</Step>
<Step>
<FaultRules/>
<Name>GeoCodeClient</Name>
</Step>
to add to what's already been said, if you need base64 encoding (and you probably will if you're using Basic Authorization), you'll need to do script callout. For instance, you can use the following Python:
import base64
if (client_secret is not None):
data = client_id + ":" + client_secret
header_value = base64.b64encode(data)
header_value = "Basic " + header_value
flow.setVariable("request.header.Authorization", header_value)
JS will be a little trickier since you need to include appropriate libraries, but I'm sure SO has plenty of more examples to follow for that.
Using Key Value Map to store sensitive data in a secure way
Step 1)Use below API to Create/Update the key Value maphttps://api.enterprise.apigee.com/v1/o/{orgname}/environments/{env}/keyvaluemaps Body:-{
"entry" : [ {
"name" : "basic_auth_system1",
"value" : "Basic XXXXXXXXXXX"
} ],
"name" : "system1_credentials"
}
Step 2) Policy used to lookup The key Value map
<KeyValueMapOperations enabled="true" continueOnError="false" async="false" name="keymap_get_credentials" mapIdentifier="system1_credentials">
<DisplayName>keymap_get_credentials</DisplayName>
<FaultRules/>
<Properties/>
<ExpiryTimeInSecs>-1</ExpiryTimeInSecs>
<Get assignTo="basic_auth_system1">
<Key>
<Parameter>basic_auth_system1</Parameter>
</Key>
</Get>
<Scope>environment</Scope>
</KeyValueMapOperations>

Resources