Spring - oauth2 - exchange authorization code for refresh and access tokens - java

I want to exchange authorization code for refresh and access tokens with Spring oauth2 client package. My code looks like:
public static void main(String[] args) {
AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails();
resource.setUserAuthorizationUri("http://localhost:8080/oauth/authorize");
resource.setAccessTokenUri("http://localhost:8080/oauth/token");
resource.setClientId("my-client-with-secret");
resource.setClientSecret("secret");
AccessTokenRequest request = new DefaultAccessTokenRequest();
request.setAuthorizationCode("o9subu");
AuthorizationCodeAccessTokenProvider provider = new AuthorizationCodeAccessTokenProvider();
OAuth2AccessToken accessToken = provider.obtainAccessToken(resource, request);
System.out.println(accessToken.getValue());
}
The provider is from https://github.com/spring-projects/spring-security-oauth/tree/master/tests/annotation/approval and I've taken the temp oauth2 code from the browser when I called the provider directly.
And the error message is
Exception in thread "main" error="invalid_request", error_description="Possible CSRF detected - state parameter was required but no state could be found"
at org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider.getParametersForTokenRequest(AuthorizationCodeAccessTokenProvider.java:255)
at org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider.obtainAccessToken(AuthorizationCodeAccessTokenProvider.java:209)
I still want to request for access_token and secret_token by myself because I obtain the oauth2 code from another system.

First of all, to get the refresh_token you must have got a code that will let you get it, to do that set "&access_type=offline&approval_prompt=force" inside the url you send to get the code.
Then you can ask for refresh_token setting this parameters
code=your_code_generated
client_id=your_client_id
client_secret=your_client_secret
redirect_uri=https://oauth2.example.com/code
grant_type=authorization_code
POST https://accounts.google.com/o/oauth2/token
You can see more detail in Google Identity Platform https://developers.google.com/identity/protocols/OAuth2WebServer

Simple add this line after the set Authorization Code:
request.setPreservedState(new Object());

Related

Scope Issue in Azure Graph Rest API Java

I am new using Azure Graph Rest API Java using this repo.
My aim is to list all of the users in the AAD tenant
So far I was only able to get to this:
List<String> scopes= Arrays.asList("https://graph.microsoft.com/User.Read.All");
AzureProfile profile = new AzureProfile(tenantId, subscriptionId, AzureEnvironment.AZURE);
final ClientSecretCredential credential = new ClientSecretCredentialBuilder()
.clientId(clientId)
.clientSecret(clientSecret)
.tenantId(tenantId)
//.httpClient(client)
.authorityHost(profile.getEnvironment().getActiveDirectoryEndpoint())
.build();
TokenCredentialAuthProvider tokenCredentialAuthProvider = new TokenCredentialAuthProvider(scopes, credential);
GraphServiceClient<Request> graphClient =
GraphServiceClient
.builder()
.authenticationProvider(tokenCredentialAuthProvider)
.buildClient();
UserCollectionPage users = graphClient.users()
.buildRequest()
.get();
for(User user: users.getCurrentPage()){
System.out.println(user.displayName);
System.out.println(user.id);
System.out.println(user.userPrincipalName);
}
However, I run into this error instead:
Caused by: java.io.IOException:
java.util.concurrent.ExecutionException:
com.microsoft.aad.msal4j.MsalServiceException:
AADSTS1002012: The
provided value for scope https://graph.microsoft.com/User.Read.All
openid profile offline_access is not valid. Client credential flows
must have a scope value with /.default suffixed to the resource
identifier (application ID URI).
It seems the Scope that I have used is wrong/insufficient, but I am not too sure what should I use the scope with. Any idea?
It is written in the documentation that:
Client credentials requests in your client service must include
scope={resource}/.default. Here, {resource} is the web API that your
app intends to call, and wishes to obtain an access token for. Issuing
a client credentials request by using individual application
permissions (roles) is not supported. All the app roles (application
permissions) that have been granted for that web API are included in
the returned access token.
The Client Credential flow is best suited for situations where you have a Deamon App that will have to authenticate and get access to some kind of a resource through a Non-Interactive way, which in sequence means that the permissions for this Deamon App have been configured and consented from a step done prior to the auth request.
The /.default scope can be translated as the request of the Background App that runs unattended, to get the bulk of the permissions that it has been configured with and access the resource that it asks.
In plain english, the use of the above scope in the Client Credentials flow is a convention that has to be implemented always when this flow is chosen :P.
I tried to reproduce the same in my environment via Postman and got below results:
I registered one Azure AD application and added API permissions like below:
When I tried to generate access token with same scope as you via Postman using client credentials flow, I got same error as below:
POST https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
grant_type:client_credentials
client_id: <appID>
client_secret: <secret_value>
scope: https://graph.microsoft.com/User.Read.All openid profile offline_access
Response:
To resolve the above error, you must change your scope to https://graph.microsoft.com/.default if you are using client credentials flow.
After changing the scope, I'm able to generate access token successfully like below:
POST https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
grant_type:client_credentials
client_id: <appID>
client_secret: <secret_value>
scope: https://graph.microsoft.com/.default
Response:
When I used the above token to call below Graph query, I got the list of users with display name, id and user principal name successfully like below:
GET https://graph.microsoft.com/v1.0/users?$select=displayName,id,userPrincipalName
Response:
In your case, change scope value in your code like below:
List<String> scopes= Arrays.asList("https://graph.microsoft.com/.default");

JWT authentication in vertx throwing unauthorized

In my application, i'm using react with keycloak authentication. And i'm trying to secure my back end vertx APIs using the received token from keycloak.
I followed this blog http://paulbakker.io/java/jwt-keycloak-angular2/
i'm calling like this from react
return await fetch('/api/getAuthentication',{
method: 'GET',
headers: {"Authorization" : `Bearer ${token}`}
})
}
And in my vertx side
JsonObject authConfig = new JsonObject()
.put("public-key", "MyPublicKeyFromKeycloak")
.put("permissionsClaimKey", "realm_access/roles");
JWTAuth authProvider = JWTAuth.create(vertx, new JWTAuthOptions(authConfig));
router.route("/api/*").handler(JWTAuthHandler.create(authProvider));
And I tried to test with below code,
router.route("/api/getAuthentication").handler(ctx -> {
logger.info("Headers: {}", ctx.request().headers().get("Authorization"));
logger.info(ctx.user().principal().encodePrettily());
});
I cannot see any log,in my browser the response is "unauthorized" with 401 code.
first of all, JWTAuthOptions will do nothing with what you have set under key "public-key" as it does not have any property that maps to it (even if it was possible in Java to have a - in property names). So either set your values on JWTAuthOptions directly using setters or consult documentation for proper keys.
Second, if your Keycloak is setup with OIDC, then you can simply use OpenIDConnectAuth class that discovers the necessary parameters from Keycloak automatically. Which is way easier.

Implementing auto-post in linkedin using spring boot

I am implementing auto-post feature in linkedin using spring boot and oauth2. I have created the app in linkedin and the permissions it request are r_liteprofile, w_member_social. I am able to get access_token but when i am sending POST request to https://api.linkedin.com/v2/ugcPosts along with the token then the server is responding with 403 Forbidden status. What am i doing wrong?
Does we need any more permission than r_liteprofile, w_member_social to post in linkedin?
TokenResponse tokenResponse = gson.fromJson(token, TokenResponse.class);
String post_url = "https://api.linkedin.com/v2/ugcPosts";
HttpPost wallPost = new HttpPost(post_url);
ArrayList mediaList = new ArrayList();
wallPost.setHeader("Content-Type","application/json");
wallPost.setHeader("Authorization","Bearer "+tokenResponse.getAccess_token());
wallPost.setHeader("X-RestLi-Protocol-Version","2.0.0");
I have used HttpClient to send the request along with above headers.
The following image shows the token and permissions.
There is nothing wrong with the OAuth token as if there was the response would be 401 (https://developer.linkedin.com/docs/v2/oauth2-client-credentials-flow)
I think you need to add 'w_share' scope to your application. Current scopes do now allow posting updates to LinkedIn. See details here: https://learn.microsoft.com/en-us/linkedin/shared/references/migrations/default-scopes-migration

Pass Crendentials from dotNet client to Java Web Service

I have a dot net application that call a java web service. I am trying to implement authentication by passing credentials to the java service. Here is the dot net code setting the credentials. How can I get these credentials in my java application? They aren't set in the headers...
System.Net.NetworkCredential serviceCredentials = new NetworkCredential("user", "pass");
serviceInstance.Credentials = serviceCredentials;
serviceInstance is an instance of SoapHttpClientProtocol.
I've tried injecting the WebServiceContext like so
#Resource
WebServiceContext wsctx;
and pulling the crentials from the headers but they aren't there.
You are not passing the credentials to your service the correct way. In order to get the Authorize http request header do the following:
// Create the network credentials and assign
// them to the service credentials
NetworkCredential netCredential = new NetworkCredential("user", "pass");
Uri uri = new Uri(serviceInstance.Url);
ICredentials credentials = netCredential.GetCredential(uri, "Basic");
serviceInstance.Credentials = credentials;
// Be sure to set PreAuthenticate to true or else
// authentication will not be sent.
serviceInstance.PreAuthenticate = true;
Note: Be sure to set PreAuthenticate to true or else authentication will not be sent.
see this article for more information.
I had to dig-up some old code for this one :)
Update:
After inspecting the request/response headers using fiddler as suggested in the comments below a WWW-Authenticate header was missing at the Java Web Service side.
A more elegant way of implementing "JAX-WS Basic authentication" can be found in this article here using a SoapHeaderInterceptor (Apache CXF Interceptors)

Oauth problems with Scribe

I'm using scribe for making an app that has oauth support. I didn't found problems With Twitter, but when using facebook I have problems...
This is the code that works on twitter oauth
OAuthService s = /* ... Facebook oauth init ... */
final Token requestToken = s.getRequestToken();
final String authURL = s.getAuthorizationUrl(requestToken);
It gives me an error at the second line:
12-20 10:01:31.475: E/AndroidRuntime(5405): java.lang.UnsupportedOperationException: Unsupported operation, please use 'getAuthorizationUrl' and redirect your users there
12-20 10:01:31.475: E/AndroidRuntime(5405): at org.scribe.oauth.OAuth20ServiceImpl.getRequestToken(OAuth20ServiceImpl.java:45)
I know that it says that I might use getAuthorizationUrl... But I have to pass a requestToken...
Could you please help me?
It would be helpful any example with Scribe and Facebook
Thanks!
PS: Same problem with Windows Live ! =(
EDIT:
I have been looking at the source code of the Scribe library and I found something
https://github.com/fernandezpablo85/scribe-java/blob/master/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java
Here we can see that I can call the getAuthorizationUrl(...) with null parameter because it doesn't use it.... But I think the prioblem now is that the config isn't filled...
here is how I initialize facebook service:
new ServiceBuilder()
.provider(FacebookApi.class)
.apiKey(....)
.apiSecret(....)
.scope("email,offline_access")
.callback("oauth://facebook")
.build();
Is this correct?
Thanks!
private static final Token EMPTY_TOKEN = null;
OAuthService service = new ServiceBuilder()
.provider(FacebookApi.class)
.apiKey(apiKey)
.apiSecret(apiSecret)
.callback("http://www.example.com/oauth_callback/")
.build();
String authorizationUrl = service.getAuthorizationUrl(EMPTY_TOKEN);
All you now need to redirect user to this URL and let him verify them self to get code from facebok.
There are a good amount of example with very good documentation for almost all major Oath
system
here is for Facebook
FacebookExample
For all major providers
Scribe Example Directory
Edit
After looking at the discussion my suggestion is to get a full understanding about Oauth1 and Oauth2.
You've got it all wrong. OAuth 2 Protocol never returns a request token like OAuth 1.
Oauth 1 does an HTTP POST request and returns an unauthorized request token. Then, you will have to authorize your unauthorized token to receive an authorized token (That's 2 HTTP call).
Oauth 2, on the other hand, doesn't have a request token flow, you will need to do an HTTP GET for an authorization token (1 HTTP call only). Hence why Scribe says that you need to call getAuthorizedUrl.
See this Facebook Example, to see how to retrieve an authorized token using OAuth 2.

Categories