Spring OAuth2 google login + username & password login - spring

I'm creating a Spring Boot REST API for a website/app, and want to give the users the ability to login using either username & password or using google.
Username + password login
POST /oauth/token + basic auth with APP client_id:client_secret
grant_type = password
username = app username
password = app password
client_id = app client id (not google client)
After this post request, the frontend (React app) should get a JWT for accessing resources on resource server. (google is not involved)
Google login
Obtain authorization_code from google using google login and google client_id
POST /oauth/token + basic auth with APP client_id:client_secret
grant_type = authorization_code
client_id = app client id (not google)
code = {authorization code from google login}
Server exchanges google authorization_code for access token and retrieves user info.
The server creates/updates app user using google user info
User is presented with a google login screen which will return an authorization code that can later be exchanged for a google access token. The authorization code is the sent to server using the /oauth/token endpoint, so server can exchange the code for a token in order to get the users info.
So far i have the username/password flow working using OAuth2 password flow and this configuration in authorization server config:
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("testclient")
.secret(passwordEncoder.encode(""))
.scopes("read", "write", "trust")
.authorities("create_users")
.authorizedGrantTypes("client_credentials", "password", "refresh_token", "authorization_code")
.accessTokenValiditySeconds(1200)
.refreshTokenValiditySeconds(50000);
}
I have added the "authorization_code" grant type to the client, but can't figure out how to authenticate using that grant type with postman.
I have tried calling google api to get the authorization code and then calling my API with:
/oauth/token?grant_type=authorization_code&code={google_auth_code}
But i just get an error: Invalid authorization code
I was hoping that Spring would call google api using the authorization code and fetch the user info.
Did I misunderstand something?

Related

springboot APIs to use Auth0

We are trying to use auth0 for spring-boot application authentication.
Created Regular Web Application and Machine to Machine Applications in auth0.com and added users under User Management.
Intention is to have a login API to authenticate users and get the access-token after the successful authentication. Use access token (as bearer token) to access other APIs of the spring-boot application.
We provided proper login and callback urls under the Machine To Machine application configuration in auth0.com.
While generating bearer token, apart from client_id, client_secret we have provided grant_type (as client_credentials), audience as https://<>/api/v2 and scope with (openid profile my_scope email roles).
We are getting 401 error while accessing the other APIs using bearer token generated using client_id, client_secret, grant_type and audience.
Wherein, we are getting 403 error while accessing the other APIs using bearer token generated using client_id, client_secret, grant_type, audience and scope.
403 error stack is as below
Client is not authorized to access <<application-domain-in-auth0>>/api/v2/. You need to create a client-grant associated to this API.
We referred to the udemy session (https://www.udemy.com/course/build-secure-apis-with-auth0-and-postman/learn/lecture/12716335#overview)
Any inputs on the overall approach and where we are going wrong.
Thanks
Venkata Madhu
not sure if it can help, but found this more relevant to the problem statement.
https://community.auth0.com/t/how-to-generate-a-management-api-token-automatically/6376
There are a few things you need to do/check:
Create a non-interactive client in Auth0, which will be used to represent your service.
Authorize the non-interactive client to call the Auth0 Management API:
Dashboard > APIs > Auth0 Management API > Non Interactive Clients > Authorize your client
Ensure that the parameters used in the call to /oauth/token are for your non interactive client:
{
grant_type: 'client_credentials',
client_id: 'NON-INTERACTIVE-CLIENT-ID',
client_secret: 'NON-INTERACTIVE-CLIENT-SECRET',
audience: 'https://yourdomain.auth0.com/api/v2/" }
Make sure the access token you receive is passed in the Authorization header for every request made to the Management API. Authorization: Bearer <access_token>

How to get username / userinfo in Spring OAuth2 Resource Server

I have an api which uses AD Token for authorization.
I am trying to fetch the username of the user inside my service component. But im failing to. I have tried this.
val authentication: Authentication = SecurityContextHolder.getContext().authentication
println(authentication.name) // Random short string with 3 "-". Not JWT
println(authentication.details.toString()) // WebAuthenticationDetails [RemoteIpAddress=127.0.0.1, SessionId=null]
println(authentication.authorities.toString()) // Prints Scope [SCOPE_User.Read]
println(authentication.principal) // org.springframework.security.oauth2.jwt.Jwt#xxxxxxxx
The token is from AD and it does contain userdata. The payload contains these fields with user related stuff. I removed the rest.
{,
"family_name": "Wick",
"given_name": "John",
"name": "WickJohn",
"roles": [
"User"
],
"scp": "User.Read",
"unique_name": "wickjo#gmail.com",
"upn": "wickjo#gmail.com",,
}
Anyone have any idea?
I solved it easily just reading the jwt manually.
val authentication = SecurityContextHolder.getContext().authentication
val jwt = authentication.principal as Jwt
println(jwt.claims["name"])
Still would be interesting to find out why i didnt get it automatically
To get username/user info in Spring OAuth2 Resource Server, please try the below:
Make sure to configure resource server in the Authorization Server too.
To get user info by token, resource server provides a filter OAuth2AuthenticationProcessingFilter
The generated token is set into SecurityContextHolder.
Otherwise, When accessing userinfo try including the access token in the header (Authorization Bearer).
If the above doesn't work, then try using On Behalf Of Flow and the code mentioned in this GitHub blog.
For more information, please refer below links:
Azure AD 2.0 Troubleshooting – OAuth Architecture Guidance (authguidance.com)
How to get userinfo by jwt token in spring security oauth2 authorization server? - Stack Overflow

Spring boot webapp as client of Google Oauth2

I'm writing a webapp using Spring Boot. For my project, I need to connect to Google for retrieving calendar information about users.
So, I integrate Spring security Oauth2 for user connection and after I want to use the Google calendar API.
The connection is ok and after it, Google redirects to a page of my webapp defined by this controller:
#Controller
class ConnectedController {
private String token
#RequestMapping("/welcome")
String welcome(Principal principal,
Model model) {
OAuth2Authentication auth = principal
model.addAttribute("user", auth.userAuthentication.details)
// credential
def api = new CalendarApi()
token = auth.details.tokenValue
println "Token: [$token]"
api.fetch(token)
return 'welcome'
}
}
For the Calendar API, I need the access token value and the refresh token value. I find the access token value in the principal object but where is the refresh token?
For information, I configured the authorization like below to have the resfresh token (normally):
userAuthorizationUri: https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&prompt=consent
As I interpret the google Oauth2 doc you get a authorization code when consent is given by the user. And this code should be used to get an access token and a refresh token from the API.
The result is an authorization code, which the application can
exchange for an access token and a refresh token.
Have you tried this?

Spring OAuth2: support auth and resource access with both SSO and custom auth server

I've found similar issue but it's unanswered, so I suppose I'm going to duplicate question a little.
I am using Spring OAuth2 to implement separate resource and custom authentification servers.
I've already configured interaction with auth server through issuing&validating JWT tokens and everything seems fine.
Now I'm trying to add SSO functionality but really stuck with it. I've researched the official Spring examples and attached guide but it is very short worded when it comes to connecting SSO part with custom server authentication. And actually author uses only external provider resource ('user' info) to show process.
I think it is normal thing to have all this SSO means of authentication and also custom registration. I can see it works well with stackoverflow for example.
I am loking for directions where to find any info about handling on resource server different kind of tokens issued by multiply SSO providers and also from custom auth server.
Maybe I can use auth chain to do this and some mean to distinguish token format to know how to process it. Is it possible with Spring OAuth2? Or I need to do this magic somehow manually?
For now I have just one 'maybe strange' idea:
To not involve my own resource server with this SSO stuff at all. After receiving Facebook (for example) token - just exchange it for api JWT token with custom auth server (associating or creating user on the way) and then work with resource server on standard basics
EDITED:
I've found at least something. I've read about configuring filters in authorization chain and translate given social tokens to my custom JWT-s as 'post authenticate'(not a crazy idea after all). But it mostly done with SpringSocial.
So now question is: how to do that?
Forgot to say that I am using Password Grant for authentication on custom server. Clients will be only trusted application and I do not even sure about browser client (thinking about only native mobile options). Even if I decide to have browser client I'll make sure it's going to have backend to store sencetive information
Ok, so after struggling to implement such behavior I've stuck with two different libraries (Spring Social & OAuth2). I decided to go my own way and do it with just Spring OAuth2:
I have the resource server, authentication server and client(backed up by Java and uses OAuth2 Client library, but it can be any other client) - my resources can be consumed only with my own JWT auth token given by my own auth server
in a case of a custom registration: client obtains JWT token(with refresh token) from auth server and sends it to the res server. Res server validates it with public key and gives the resource back
in a case of SSO: client obtains Facebook(or other social platform token) and exchanges it for my custom JWT token with my custom auth server. I've implemented this on my auth server using custom SocialTokenGranter(currently handles facebook social token only. For every social network I'll need separate grant type). This class makes an additional call to facebook auth server to validate token and obtain user info. Then it retrieves the social user from my db or creates new and returns JWT token back to the client. No user merging is done by now. it is out of scope for now.
public class SocialTokenGranter extends AbstractTokenGranter {
private static final String GRANT_TYPE = "facebook_social";
GiraffeUserDetailsService giraffeUserDetailsService; // custom UserDetails service
SocialTokenGranter(
GiraffeUserDetailsService giraffeUserDetailsService,
AuthorizationServerTokenServices tokenServices,
OAuth2RequestFactory defaultOauth2RequestFactory,
ClientDetailsService clientDetailsService) {
super(tokenServices, clientDetailsService, defaultOauth2RequestFactory, GRANT_TYPE);
this.giraffeUserDetailsService = giraffeUserDetailsService;
}
#Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails clientDetails, TokenRequest request) {
// retrieve social token sent by the client
Map<String, String> parameters = request.getRequestParameters();
String socialToken = parameters.get("social_token");
//validate social token and receive user information from external authentication server
String url = "https://graph.facebook.com/me?access_token=" + socialToken;
Authentication userAuth = null;
try {
ResponseEntity<FacebookUserInformation> response = new RestTemplate().getForEntity(url, FacebookUserInformation.class);
if (response.getStatusCode().is4xxClientError()) throw new GiraffeException.InvalidOrExpiredSocialToken();
FacebookUserInformation userInformation = response.getBody();
GiraffeUserDetails giraffeSocialUserDetails = giraffeUserDetailsService.loadOrCreateSocialUser(userInformation.getId(), userInformation.getEmail(), User.SocialProvider.FACEBOOK);
userAuth = new UsernamePasswordAuthenticationToken(giraffeSocialUserDetails, "N/A", giraffeSocialUserDetails.getAuthorities());
} catch (GiraffeException.InvalidOrExpiredSocialToken | GiraffeException.UnableToValidateSocialUserInformation e) {
// log the stacktrace
}
return new OAuth2Authentication(request.createOAuth2Request(clientDetails), userAuth);
}
private static class FacebookUserInformation {
private String id;
private String email;
// getters, setters, constructor
}
}
And from class extending AuthorizationServerConfigurerAdapter:
private TokenGranter tokenGranter(AuthorizationServerEndpointsConfigurer endpoints) {
List<TokenGranter> granters = new ArrayList<>(Arrays.asList(endpoints.getTokenGranter()));
granters.add(new SocialTokenGranter(giraffeUserDetailsService, endpoints.getTokenServices(), endpoints.getOAuth2RequestFactory(), endpoints.getClientDetailsService()));
return new CompositeTokenGranter(granters);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
...
.allowFormAuthenticationForClients() // to allow sending parameters as form fields
...
}
Every JWT token request is going to 'host:port + /oauth/token' url
Depending on 'Grant type' the server will handle such requests differently. Currently I have 'password'(default), 'refresh_token' and 'facebook_social'(custom) grant types
For default 'password' Grant type the client should send next parameters:
clientId
clientSecret (depends of the client type. Not for single-page clients)
username
password
scope (if not explicitly set in auth server configuration for current client)
grantType
For 'refresh_token' Grant type the client should send next parameters:
clientId
clientSecret (depends of the client type. Not for single-page clients)
refresh_token
grantType
For 'facebook_social' Grant type the client should send next parameters:
clientId
facebook_social_token (custom field)
grantType
Based on the client design the way to send these requests will be different.
In my case with test Java based client which uses Spring OAuth2 library to obtain the social token I do the token exchange procedure with the redirect in controller(controller being invoked using url defined in facebook dev page configuration).
It can be handled in two stages: after obtaining facebook social token JavaScript can make a separate explicit call to my auth server to exchange tokens.
You can see Java client implementation examples here, but I doubt that you're going to use Java client in production:https://spring.io/guides/tutorials/spring-boot-oauth2/

Sending Bearer Tokens to Web API via Postman

Update
I have been able to get a Bearer token using instructions from this thread
Here are the instructions in Postman:
Url: https://login.windows.net/[tenantname].onmicrosoft.com/oauth2/token
Type: POST
Headers: none
Body: form-data
grant_type: client_credentials
client_id: [client-id]
client_secret: [client-secret]
However, if I send the same token in my call to a Web API endpoint, I still get back "Authorization has been denied for this request"
Why is it still not authorizing ?
End Update
I have created an ASP.Net Web API project which is protected using an organizational Azure AD instance. I have set up the tenant id, client id and secret correctly.
The Azure AD instance is the same one backing our Office 365/SharePoint instance and the idea is to create SharePoint Add-Ins which can call the services using the logged in user's context.
I am stuck at testing the API. I can call unauthorized endpoints without any issue. However, when I add the [Authorize] attribute, I always get back this response: "Authorization has been denied for this request."
As I understand it, I need to generate a bearer token and add it to my Postman request in the header (see image). After much Googling, I still have not been able to make this work.
My question is: How do I generate a bearer token for a Web API instance protected by Azure AD.
My configuration code is as below:
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters {
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
},
});
}
First, you can use POSTMAN to test web api protected by the Bearer token. Here's my postman screenshot:
POSTMAN sending bearer token to web api
Basically: in the request header, specify the key as "Authorization", and value as "Bearer [your token". IF you run into errors, look the headers of the response and you'll see more detailed error info.
Note, most tokens have an expiration period, you can try to verify if your token is valid. e.g. https://jwt.io/

Resources