When User Sign In Gmail account via Oauth2 protocol and finish it, my server get authorization code and I make exchange this code for refresh token and access token, everything works as planned but I need to get email address too. I mean if user logged in as helloworld#gmail.com, somehow with authorization code I would like to know this address, may I somehow to know it?
This is endpoint where I exchange authorization code on access token and refresh token:
public OAuth2AccessToken oauth(String authorizationCode) {
AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails();
resource.setUserAuthorizationUri(userAuthorizationUri);
resource.setAccessTokenUri(accessTokenUri);
resource.setClientId(clientId);
resource.setClientSecret(clientSecret);
resource.setPreEstablishedRedirectUri(redirectUrl);
resource.setScope(scopes);
resource.setUseCurrentUri(false);
AccessTokenRequest request = new DefaultAccessTokenRequest();
request.setPreservedState(new Object());
request.setAuthorizationCode(authorizationCode);
AuthorizationCodeAccessTokenProvider provider = new AuthorizationCodeAccessTokenProvider();
OAuth2AccessToken accessToken = provider.obtainAccessToken(resource, request);
return accessToken;
}
I don't have WebSecurityConfigurerAdapter for OAuth2
If the user's email address is not already provided in the id_token part of the oauth2 response, you can use the Gmail API Users.getProfile operation, using the special value "me" as the userId to refer to the authenticated user.
See: https://developers.google.com/gmail/api/v1/reference/users/getProfile
That should give you a response like:
{
"emailAddress": -string-,
"messagesTotal": -integer-,
"threadsTotal": -integer-,
"historyId": -unsigned long-
}
Related
I have created web api method to insert some data. It works fine anonymously. But when I decorate the method with [Authorize] , I recieve message like "Message": "Authorization has been denied for this request." jwt bearer token was entered with the request.I run the function using Postman. Should I add any class to validate the bearer token?
[Authorize]
[Route("customer")]
public String customer(APICustomer APICustomer1)
{
try
{
insert_Customer(APICustomer1.custid, APICustomer1.custname, APICustomer1.status);
}
catch (Exception ex)
{
}
return "1";
}
Authentication is the process of validating user credentials and authorization is the process of checking privileges for a user to access specific modules in an application.
Please read the article for more detailed information: Authentication And Authorization In ASP.NET Core Web API With JSON Web Tokens
I have a functional project of chat by websockets that is already working, now I need to implement authentication by JWT. This is the flow of the requests
Client connects to ws://localhost:port/chatting
Client subscribes to its own inbox, in order to receive messages from other clients: /user/{userId}/queue/chatting
Client can send messages to other users in /app/message by specifying in body:
{ 'message': msg, 'from': "userId", 'to': "recipientId" }, and the system will redirect them to /user/{recipientId}/queue/chatting
I would like to secure:
A user has to authenticate in order to do the handshake
A user has to be "userId" in order to subscribe to /user/"userId"/queue/chatting, so that other users can't access it.
A user has to be "userId" in order to send a message to another user with "userId" in from inside body
First of all adding authentication can be done by configuring the same in WebSecurityConfig , adding a custom JwtRequestFilter which is going to parse you JWT token and set the authentication context for that request.
A good reference for the same: create-apis-with-jwt-authorization-using-spring-boot.
Regarding 2 and 3, Springboot does not allow exposing dynamic endpoints for STOMP registration, so you will have to expose /user/queue/chatting and clients will have to directly subscribe to this. However, using convertAndSendToUser(userId, destination, payload) you can send the message based on the userId. This function internally calls this function which does this this.destinationPrefix(/user) + user(/username) + destination(/queue/chatting) so as you can see the variable user is getting prefixed to final destination, you can pass userId instead of userName.
Reference for this: simpMessagingTemplate.convertAndSendToUser
But do note in this case you would have to set the username for this session as userId itself. This can be done in your custom JwtRequestFilter
private void populateSecurityContextHolder(HttpServletRequest request, String userId, List<SimpleGrantedAuthority> grantedAuthorities) {
PreAuthenticatedAuthenticationToken authToken = new PreAuthenticatedAuthenticationToken(
userId, null, grantedAuthorities);
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authToken);
log.info("Authentication set for userId " + userId);
}
So with this, your server can send messages based on userId it receives in the messages.
I am developing a C# Web Api (.NET Framework) and would like to use in parallel the AAD authentication (already working correctly) and Google Authentication. This means, my clients (javascript or just Postman) should fetch the token, include it in the Authorization header (Bearer token) and be able to execute the API methods.
However, it seems that the token I am generating with Postman is not accepted by my C# Web Api. I am always getting a HTTP 401. The AAD (Windows Azure Active Directory) works seamlessly.
Using a C# Web Api (.Net Framework 4.6.1), including Microsoft.Owin.Security.Google Nuget package v.4.0.1.
I have created the Oauth client on the google developer console, got my client ID and client secret. Also I have set up the redirect URI there.
I am using Postman Oauth2 authorization, setting following parameters:
Grant type: implicit
Callback URL: my url
AUth URL: https://accounts.google.com/o/oauth2/v2/auth
Client ID: 65xxxxxmyclientid.apps.googleusercontent.com
scope: openid email profile
Then I can log in with my google account, give consent to use the scopes, and am getting the token like this:
Access Token
ya29.Gls7....
This token is then sent as Authorization header like "Bearer ya29.Gls7...."
Startup.Auth
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
},
});
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
ClientId = ConfigurationManager.AppSettings["GoogleClientId"],
ClientSecret = ConfigurationManager.AppSettings["GoogleClientSecret"],
});
}
ValuesController
public class ValuesController : ApiController
{
[Authorize]
// GET api/values/5
public string Get(int id)
{
return "value";
}
}
Every time I call the API using Postman, I am getting a 401, even though the bearer token is included in the request authorization header.
HTTP GET https://localhost:44385/api/values/1
Edit: There is no custom code from my side which would validate the token. I assumed that is being done automatically by the library. In fact, this is done automatically for AAD tokens, but not in the case of Google Oauth.
I expect to get inside the code of the controller method, having at least the identity name set with the google account name/email.
What am I missing to be authenticated inside my controller method with google account?
i have implemented GoogleAuthorizationCodeFlow scenario from google api client dotnet and tutorial to get token from what my client sent to server as a code. but when i call flow.ExchangeCodeForTokenAsync , I get the following error :
{"Error:\"invalid_grant\", Description:\"\", Uri:\"\""}
I read google authorization invalid_grant and gusclass oauth 2 using google dotnet api client libraries but they didn't help me and. I think it must be very simple but I don't know why it doesn't work.
For client side , I have used Satellizer and this is my server Codes:
public bool PostExchangeAccessToken(GoogleClientAccessCode code)
{
string[] SCOPES = { "email" };
IAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets()
{
ClientSecret = "******",
ClientId = "********.apps.googleusercontent.com"
},
Scopes = SCOPES
});
try
{
TokenResponse token;
token = flow.ExchangeCodeForTokenAsync("*****#gmail.com", Newtonsoft.Json.JsonConvert.SerializeObject(code), "https://localhost:44301/",
CancellationToken.None).Result;
}
catch (Exception ex)
{
throw ex;
}
return true;
}
what is the problem?
On Github I found that I must use the Token from the client and use
GoogleAuthorizationCodeFlow.Initializer()
to create my UserCredential object.
You can check your google developer console settings.(Authorized redirect URIs)
Credentials => OAuth 2.0 client IDs => Your Application Settings => Authorized redirect URIs
You must add url. ("https://localhost:44301/")
My code :
flow.ExchangeCodeForTokenAsync("me", authCode, redirectUri, CancellationToken.None).Result;
Authorized redirect URIs
For use with requests from a web server. This is the path in your application that users are redirected to after they have authenticated with Google. The path will be appended with the authorization code for access. Must have a protocol. Cannot contain URL fragments or relative paths. Cannot be a public IP address.
I'm using Google API to obtain access and refresh tokens for offline access, however, the refresh token is always null.
The authorizationCode comes from the client, so I exchange it with the tokens on the server:
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT,
JSON_FACTORY,
clientId,
clientSecret,
scopes)
.setAccessType("offline")
.build();
GoogleTokenResponse response = flow
.newTokenRequest(authorizationCode)
.setRedirectUri(redirectUri)
.execute();
// response has access_token and id_token. don't see any refresh token here
return flow.createAndStoreCredential(response, null);
// this returns Credential with refreshToken = null
Client code:
$(document).ready(function() {
gapi.load('auth2', function() {
auth2 = gapi.auth2.init({
client_id: '<my client id>',
// Scopes to request in addition to 'profile' and 'email'
scope: 'https://www.google.com/m8/feeds https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/drive.appfolder'
});
});
$('#signinButton').click(function() {
auth2.grantOfflineAccess({'redirect_uri': 'postmessage'}).then(onSignIn);
});
});
// the onSignIn function sends the one time code (i.e. authResult['code']) to the server
Am I doing anything wrong here?
I saw this question: Google OAuth: can't get refresh token with authorization code but it didn't really answer my question and I'm not aware of refresh token only being created the first time a user logs in.
Edit:
I believe this is actually a client side problem, because the consent window doesn't ask for offline access. Will update this question based on the results of a new question here: Google javascript sign-in api: no offline access