Google authentification always ask authorization - oauth

After having connected a user from Google OAuth, when this one wishes to reconnect during a next session requiring the selection of his google account, the permission is asked again.
According to the documentation, the behavior of the parameter prompt that is responsible for authorization requests is as follows:
If no value is specified and the user has not previously authorized
access, then the user is shown a consent screen.
The user should therefore not have to reorder his consent.
The only answer envisaged was the one on this question : login with google always asks user consent
Because I also work locally without secure HTTP, but he assumes that a cookie policy is present which is not the case.
How could I do to resolve this anomaly ?
Edit :
/**
* Create a new OAuth2Client with the credentials previously loads
*/
private getOAuth2() : OAuth2Client {
return new OAuth2(
this.credentials.client_secret,
this.credentials.client_id,
this.credentials.redirect_uris[0]
);
}
/**
* Create connection URL for the given scopes
* #param scopes
*/
public url(scopes: Array<string>) : string {
return this.client.generateAuthUrl({
access_type: "offline",
scope: scopes
});
}
// The scope used to generate URL is 'https://www.googleapis.com/auth/youtube.readonly'
// this.client refer to a client which is load by getOAuth2
// and use only for the generation of URL's.

Related

.NET IdentityServer4 OpenIdConnect with Discord

I'm trying to cut my teeth with IdentityServer and have been following the guides on readthedocs closely. I'm at the point of adding external identity providers and have added all the ones I want to support to the IdentityServer project.
I specifically want to include "guilds" from Discord then do role based authorization in my web app based on the roles a user has on a specific Guild. Discord lists the various Scopes that are allowed:
So I've included the AspNet.Security.OAuth.Discord package and added an IdentityResource for guilds:
public static class AuthConfig
{
public static IEnumerable<IdentityResource> IdentityResources =>
new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Address(),
new IdentityResources.Email(),
new IdentityResources.Profile(),
new IdentityResource()
{
Name = "guilds",
DisplayName = "Discord Guilds",
Description = "All of the Discord Guilds the user belongs to",
Required = true,
Emphasize = true,
UserClaims = new[] { "name" } // <<< Not sure how I find the claims on the discord api doco
}
};
.
.
.
}
This then allows me to add scopes to my discord options in the startup of my IdentityServer project:
public void ConfigureServices(IServiceCollection services)
{
// uncomment, if you want to add an MVC-based UI
services.AddControllersWithViews();
services.AddAuthentication()
.AddDiscord("Discord", options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.ClientId = "<my client id>";
options.ClientSecret = "<my client secret>";
options.Scope.Add("guilds");
})
When I login the uri has the guild scope added and I get the warning on the acknowlegement dialog:
But when I view the content of my claims I don't see anything.
If I add a standard oidc one of email that does display though.
If I follow through to the definition of IdentityResources.Email then I see these claims defined on the ScopeToClaimsMapping property in IdentityServer4.Constants
but I'm not sure how to determine what these claims should be for the Discord guilds scope...and is this even the issue anyway.
Can anyone point me in the right direction?
Claims and Scopes are different but related things.
An scope is a claim, it talks about the scope of your access.
When you request the "guild" scope, it means your token will be able to access the information under that scope. But that doesn't necessarily mean that information is going to be presented in a claim on the token or user_info response.
Instead, what you need to do to get the "guilds" information is to consume their API, using the token.
Discord Developer Portal - Guilds
Get Current User Guilds
GET /users/#me/guilds
Returns a list of partial guild objects the current user is a member of.
Requires the guilds OAuth2 scope.

To retrieve access token

I have created a MVC application to escalate work to other person inside my organization. I have added all the members in my organization to AAD,
and registered an application there, created app service and linked that app service to registered app with SSO enabled.
Now every time someone visits the app, they can login successfully using their respective credential.
What I want to do know is to retrieve all the members in my AAD and display them inside dropdown list so that anyone can escalate to others by just looking in the dropdown list.
I have tried with sample graph SDK to get the name of users in my organization
with this code
private string redirectUri = ConfigurationManager.AppSettings["ida:RedirectUri"];
private string appId = ConfigurationManager.AppSettings["ida:AppId"];
private string appSecret = ConfigurationManager.AppSettings["ida:AppSecret"];
private string scopes = ConfigurationManager.AppSettings["ida:GraphScopes"];
public async Task<string> GetUserAccessTokenAsync()
{
string signedInUserID = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
HttpContextWrapper httpContext = new HttpContextWrapper(HttpContext.Current);
TokenCache userTokenCache = new SessionTokenCache(signedInUserID, httpContext).GetMsalCacheInstance();
//var cachedItems = tokenCache.ReadItems(appId); // see what's in the cache
ConfidentialClientApplication cca = new ConfidentialClientApplication(
appId,
redirectUri,
new ClientCredential(appSecret),
userTokenCache,
null);
try
{
AuthenticationResult result = await cca.AcquireTokenSilentAsync(scopes.Split(new char[] { ' ' }), cca.Users.First());
return result.AccessToken;
}
// Unable to retrieve the access token silently.
catch (Exception)
{
HttpContext.Current.Request.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties() { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
throw new ServiceException(
new Error
{
Code = GraphErrorCode.AuthenticationFailure.ToString(),
Message = Resource.Error_AuthChallengeNeeded,
});
}
}
with some change in scope.
<add key="ida:AppId" value="xxxxx-xxxxxxx-xxxxxx-xxxxxxx"/>
<add key="ida:AppSecret" value="xxxxxxxxxxx"/>
<add key="ida:RedirectUri" value="http://localhost:55065/"/>
<add key="ida:GraphScopes" value="User.ReadBasic.All User.Read Mail.Send Files.ReadWrite"/>
This enables me to get basic details of all user in my organization.
But how I can achieve this in my app where authentication related stuffs are done in azure only, and there is no code for authentication and authorization in entire solution.
Thanks
Subham, NATHCORP, INDIA
But how I can achieve this in my app where authentication related stuffs are done in azure only, and there is no code for authentication and authorization in entire solution.
Based on my understanding, you are using the build-in feature App Service Authentication / Authorization. You could follow here to configure your web app to use AAD login. And you need to configure the required permissions for your AD app as follows:
Note: For Azure AD graph, you need to set the relevant permissions for the Windows Azure Active Directory API. For Microsoft Graph, you need to configure the Microsoft Graph API.
Then, you need to configure additional settings for your web app. You could access https://resources.azure.com/, choose your web app and update App Service Auth Configuration as follows:
Note: For using Microsoft Graph API, you need to set the resource to https://graph.microsoft.com. Details, you could follow here.
For retrieving the access token in your application, you could get it from the request header X-MS-TOKEN-AAD-ACCESS-TOKEN. Details, you could follow Working with user identities in your application.
Moreover, you could use Microsoft.Azure.ActiveDirectory.GraphClient package for Microsoft Azure Active Directory Graph API, Microsoft.Graph package for Microsoft Graph API using the related access token.

MSAL and Azure AD: What scopes should I pass when I just want to get the user ID?

I'm using MSAL to get an ID Token which is then used to access an Web API app. I've got a couple of questions and I was wondering if someone could help me understand what's going on.
Let me start with the authentication process in the client side. In this case, I'm building a Windows Forms app that is using the following code in order to authenticate the current user (ie, in order to get an ID Token which will be used to validate the user when he tries to access a Web API app):
//constructor code
_clientApp = new PublicClientApplication(ClientId,
Authority, //which url here?
TokenCacheHelper.GetUserCache());
_scopes = new []{ "user.read" }; //what to put here?
//inside a helper method
try {
return await _clientApp.AcquireTokenSilentAsync(_scopes, _clientApp.Users.FirstOrDefault());
}
catch (MsalUiRequiredException ex) {
try {
return await _clientApp.AcquireTokenAsync(_scopes);
}
catch (MsalException ex) {
return null;
}
}
The first thing I'd like to clear is the value that should be used for the authority parameter. In this case, I'm using an URL on the form:
https://login.microsoftonline.com/{Tenant}/oauth2/v2.0/token
However, I'm under the impression that I could also get away with something like this:
https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration
It seems like one endpoint is specific to my Azure AD while the other looks like a general (catch all) URL...Where can I find more information about these endpoints and on what's the purpose of each...
Another thing that I couldn't quite grasp is the scope. I'm not interested in querying MS Graph (or any other Azure related service for that matter). In previous versions of the MSAL library, it was possible to reference one of the default scopes. However, it seems like that is no longer possible (at least, I tried and got an exception saying that I shouldn't pass the default scopes...).
Passing an empty collection (ex.: new List<string>()) or null will also result in an error. So, in this case, I've ended passing the user.read scope (which, if I'm not mistaken, is used by MS Graph API. This is clearly not necessary, but was the only way I've managed to get the authentication process working. Any clues on how to perform the call when you just need to get an ID Token? Should I be calling a different method?
Moving to the server side, I've got a Web API app whose access is limited to calls that pass an ID token in the authentication header (bearer). According to this sample, I should use something like this:
private void ConfigureAuth(IAppBuilder app) {
var authority = "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration";
app.UseOAuthBearerAuthentication(
new OAuthBearerAuthenticationOptions {
AccessTokenFormat = new JwtFormat(GetTokenValidationParameters(),
new OpenIdConnectCachingSecurityTokenProvider(authority)),
Provider = new OAuthBearerAuthenticationProvider {
OnValidateIdentity = ValidateIdentity
}
});
}
Now, this does work and it will return 401 for all requests which don't have a valid ID Token. There is one question though: is there a way to specify the claim from the Ticket's Identity that should be used for identifying the username (User.Identity.Name of the controller)? In this case, I've ended handling the OnValidateIdentity in order to do that with code that looks like this:
private Task ValidateIdentity(OAuthValidateIdentityContext arg) {
//username not getting correctly filled
//so, i'm handling this event in order to set it up
//from the preferred_username claim
if (!arg.HasError && arg.IsValidated) {
var identity = arg.Ticket.Identity;
var username = identity.Claims.FirstOrDefault(c => c.Type == "preferred_username")?.Value ?? "";
if (!string.IsNullOrEmpty(username)) {
identity.AddClaim(new Claim(ClaimTypes.Name, username));
}
}
return Task.CompletedTask;
}
As you can see, I'm searching for the preferred_username claim from the ID Token (which was obtained by the client) and using its value to setup the Name claim. Is there any option that would let me do this automatically? Am I missing something in the configuration of the OAuthBearerAuthenticationMiddleware?
Regarding your First Query -
Where can I find more information about these endpoints and on what's the purpose of each...
Answer -
https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration
The {tenant} can take one of four values:
common -
Users with both a personal Microsoft account and a work or school account from Azure Active Directory (Azure AD) can sign in to the application.
organizations -
Only users with work or school accounts from Azure AD can sign in to the application.
consumers -
Only users with a personal Microsoft account can sign in to the application.
8eaef023-2b34-4da1-9baa-8bc8c9d6a490 or contoso.onmicrosoft.com -
Only users with a work or school account from a specific Azure AD tenant can sign in to the application. Either the friendly domain name of the Azure AD tenant or the tenant's GUID identifier can be used.
Regarding your Second Query on Scope -
Answer - Refer to this document - OpenID Connect scopes
Regarding your Third Query on Claim -
Answer - Refer to this GIT Hub sample - active-directory-dotnet-webapp-roleclaims

How to tie OAuth authentication with Spring Security

I have a Grails 2.5.3 app that currently uses spring security plugin for authentication. Users login using a username/pwd.
I have updated the app now to support OAuth authentication (Using ScribeJava). Users can click a link that redirects them to OAuth providers page and upon successfully entering the credentials they are redirected back to my application. However, I have not been able to tie this functionality with spring security plugin so that when the users are redirected back to my app (after successful login from OAuth), I can actually see that they are logged in and continue to use all my spring security goodies like <sec:ifLoggedIn>.
Does anyone know of a way to do this or have an example I can take a look at?
Here is how I authenticate a user using OAuth:
//called when user clicks "login using oauth"
def authenticate() {
OAuthService service = new ServiceBuilder()
.apiKey(grailsApplication.config.my.sso.clientid)
.apiSecret(grailsApplication.config.my.sso.clientsecret)
.build(MyApi.instance());
String url = service.getAuthorizationUrl();
return redirect(url: url)
}
//called when oauth provider redirects to my application
def authorization_code() {
def code = params.code
OAuthService service = new ServiceBuilder()
.apiKey(grailsApplication.config.my.sso.clientid)
.apiSecret(grailsApplication.config.my.sso.clientsecret)
.build(MyApi.instance());
println code
OAuth2AccessToken accessToken = service.getAccessToken(code);
String userProfileUrl = grailsApplication.config.my.sso.authdomain+"/userinfo"
final OAuthRequest request = new OAuthRequest(Verb.GET, userProfileUrl);
service.signRequest(accessToken, request);
final Response response = service.execute(request);
println(response.getCode());
println(response.getBody());
render (text: code)
}
Whenever you authenticate via OAuth, the remote server return you a unique id (some numeric value) each time.
You can use that id to verify the user in your end and authenticate the user using springsecurity.reauthenticate() method.
Steps to do that :
When user connect (authenticate first time) with service provider.
Service provider send you that unique id. Save that unique id in
user table.
And when user login via that service provider. Again service provider
sends that unique id. Check if that unique id exists in your system,
and if user exists with that unique id then use
springsecurity.reauthenticate(userInstance) method to authenticate the user. And now you can use spring security features.
check out link: http://www.jellyfishtechnologies.com/grails-2-2-0-integration-with-facebook-using-grails-oauth-plugin/
Assuming you got the user details from Oauth provider you just need to
set the security context of that particular user
Just get the user details by parsing the JSON like
def oauthResponse = JSON.parse(response?.getBody())
Map data = [
id : oauthResponse.id,
email : oauthResponse.email,
name : oauthResponse.name,
first_name : oauthResponse.given_name,
last_name : oauthResponse.family_name,
gender : oauthResponse.gender,
link : oauthResponse.link
]
Well in our case we used the email id as the user name.
So when we get the user data just check if user is already registered with system or not like below
//load the user details service bean
def userDetailsService
//check if user is already registered on our system
User user = User.findByEmail(data?.email)
if (user) {
//If user exists load his context
userDetails = userDetailsService.loadUserByUsername(data?.email)
} else {
//create the new user
//Assign the role to it
//load his context as below
userDetails = userDetailsService.loadUserByUsername(data?.email)
}
After user registered successfully we just need to load his context like below
def password
//setting spring security context
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(userDetails, password == null ? userDetails.getPassword() : password, userDetails.getAuthorities()))
Once spring security context is loaded you can redirect user to your landing page.
Now oauth user will be access resources like the any other user with same role.

How to add parameters to redirect_uri in WebApi Oauth Owin authentication process?

I'm creating a webapi project with oauth bearer token authenthication and external login providers (google, twitter, facebook etc.). I started with the basic VS 2013 template and got everything to work fine!
However, after a user successfully logs is, the owin infrastructure creates a redirect with the folllowing structure:
http://some.url/#access_token=<the access token>&token_type=bearer&expires_in=1209600
In my server code I want to add an additional parameter to this redirect because in the registration process of my app, a new user needs to first confirm and accept the usage license before he/she is registered as a user. Therefore I want to add the parameter "requiresConfirmation=true" to the redirect. However, I've no clue about how to do this. I tried setting AuthenticationResponseChallenge.Properties.RedirectUri of the AuthenticationManager but this doesn't seem to have any affect.
Any suggestions would be greatly appreciated!
It should be relatively easy with the AuthorizationEndpointResponse notification:
In your custom OAuthAuthorizationServerProvider implementation, simply override AuthorizationEndpointResponse to extract your extra parameter from the ambient response grant, which is created when you call IOwinContext.Authentication.SignIn(properties, identity).
You can then add a custom requiresConfirmation parameter to AdditionalResponseParameters: it will be automatically added to the callback URL (i.e in the fragment when using the implicit flow):
public override Task AuthorizationEndpointResponse(OAuthAuthorizationEndpointResponseContext context) {
var requiresConfirmation = bool.Parse(context.OwinContext.Authentication.AuthenticationResponseGrant.Properties.Dictionary["requiresConfirmation"]);
if (requiresConfirmation) {
context.AdditionalResponseParameters.Add("requiresConfirmation", true);
}
return Task.FromResult<object>(null);
}
In your code calling SignIn, determine whether the user is registered or not and add requiresConfirmation to the AuthenticationProperties container:
var properties = new AuthenticationProperties();
properties.Dictionary.Add("requiresConfirmation", "true"/"false");
context.Authentication.SignIn(properties, identity);
Feel free to ping me if you need more details.

Resources