Need to Authenticate the Users using Twitter, Google, Facebook Apis through oauth plugin? - oauth

First i briefly want to know how you can use oauth works. What we need to pass in this plugin and what this plugin will return. Do we have to customize the plugin for different php frameworks. I have seen that their is a different extension of oauth for different framework, why is that?
I need to authenticate the users using social networks in yii framework and I have integrated eouath extension of yii to use oauth and have made an action to use access the ads service of google user like this
public function actionGoogleAds() {
Yii::import('ext.eoauth.*');
$ui = new EOAuthUserIdentity(
array(
//Set the "scope" to the service you want to use
'scope'=>'https://sandbox.google.com/apis/ads/publisher/',
'provider'=>array(
'request'=>'https://www.google.com/accounts/OAuthGetRequestToken',
'authorize'=>'https://www.google.com/accounts/OAuthAuthorizeToken',
'access'=>'https://www.google.com/accounts/OAuthGetAccessToken',
)
)
);
if ($ui->authenticate()) {
$user=Yii::app()->user;
$user->login($ui);
$this->redirect($user->returnUrl);
}
else throw new CHttpException(401, $ui->error);
}
If I want to use other services like linkedin, facebook, twitter just to sign up the user should I just change the scope and parameters or also have to make some changes elsewhere. How do I store user information in my own database?

In simple case you may use the table "identities" with fields "*external_id*" and "provider". Every OAuth provider must give unique user identificator (uqiue only for that provider). To make it unique on your site you may use pair with provider predefined name (constant). And any other additional fields (if a provider gives it).
In the same table you should store identity data of internal authorization, with provider name 'custom' (for ex.). To store password and other data use a separate table, and PK from this table would be your "*external_id*". Universal scheme.
And PHP, something like this:
class UserIdentity extends CUserIdentity
{
protected $extUserID;
public function __construct($extUserID)
{
$this->extUserID = $extUserID;
}
...
public function authenticate()
{
...
//After search $this->extUserID as PK in users table (built-in authorization)
...
$identity = Identities::model()->findByAttributes(array(
'ext_id' => $this->extUserID,
'service' => 'forum',
));
if(!count($identity))
{
$identity = new Identities;
$identity->ext_id = $this->extUserID;
$identity->service = 'forum';
$identity->username = $userData['username'];
$identity->save();
}
$this->setState('id', $identity->id);
...
}
}

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.

How client application uses the scopes and resources extracted from access token to restrict the access of API - identityserver4

I can see many links describing how to use identityserver4.
Host application:
Configuring clients with [clientId, secret, APIScopes, APIResources, IdentityResources]
Passing clients details to identityserver4
Client Application:
Passing client id to the endpoint to get access token and refresh token that contains scopes and resources of the defined clients. using that scope and resources we can restrict the access of the API's.
But I am still wondering how the client application will use the API scopes to restrict the access of the Application is there any sample how to utilize the scopes to restrict the application access?
And also approach for maintaining roles in identitserver4
I don't find any links describing how to use the client part after getting access token, please share me any reference that can help me?
In a API (AddJwtBearer), you do two things, authentication and authorization.
In the authorization stage, you check the claims (the scopes is part of the claims) found in the access token.
Can do the authorization check using the role or policy based approach.
A sample policy can look like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization(options =>
{
options.AddPolicy("ViewReports", policy =>
policy.RequireAuthenticatedUser()
.RequireRole("Finance")
.RequireRole("Management")
);
});
then you can decorate your controllers with this attribute:
[Authorize("ViewReports")]
public class SecretController : Controller
{
}
From a consumer (API) point of view, the scopes are just like all the other claims. they are not treated differently.
When using identityserver4, client needs to configure the ClientId and the authority address, this server need to configure the allowed scope corresponding to clientid.
services.AddAuthentication(config=>
{
config.DefaultScheme = "cookie";
config.DefaultChallengeScheme = "oidc";
})
.AddCookie("cookie")
.AddOpenIdConnect("oidc", config=>
{
config.Authority = "url";
config.ClientId = "client_id";
config.ClientSecret = "client_secret";
config.SaveTokens = true;
config.ResponseType = "code";
});
The Identityserver will authorize some functions according to the ClientId.
new Client
{
ClientId="client_id",
ClientSecrets=
{
new Secret("client_secret".ToSha256())
},
AllowedGrantTypes=GrantTypes.Code,
RedirectUris={ "https://localhost:[port]/signin-oidc"},
AllowedScopes={ "apione", "apitwo",
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
//...
},
RequireConsent=false
}
In apione, you still need to configure the audience.
services.AddAuthentication("jwtauth")
.AddJwtBearer("jwtauth",config=>
{
config.Authority = "identityserver url";
config.Audience = "apione";
config.RequireHttpsMetadata = false;
IdentityModelEventSource.ShowPII = true;
});
Every user has their own role, so you can add the claims after the user logs in on the server. In addition, the project api can configure the AddAuthorization as Tore Nestenius says.

Issue securig MultiTenant Application with Identity Server & SaasKit

I've created a multitenant ASP.NET Core Web API and secured by Identity Server. I've used SaasKit Multitenancy nugget for multitenancy. and multi-tenancy is working fine. I'm facing issue with the authentication.
I've used the different hostname for the different tenant. I've defined different scope per tenant and retrieve token based on the scope for the tenant. The first request to the API works fine. but then when the second tenant tries to access the API it errors out with audience validation. Token has the valid audience but the Web API still use the audience of the first request.
Here is the code in my API:
services.AddSingleton<IOptionsMonitor<IdentityServerAuthenticationOptions>, IdentityServerTenantProvider>();
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
options.RequireHttpsMetadata = false;
});
And here is the implementation of IdentityServerTenantProvider
protected override IdentityServerAuthenticationOptions Create(IdentityServerAuthenticationOptions options, string name, string tenant, string tenantHostName)
{
var currentTenantContext = this._memoryCache.Get(tenantHostName) as TenantContext<PaperSaveAPITenant>;
options.Authority = currentTenantContext.Tenant.Authority;
options.ApiName = currentTenantContext.Tenant.ApiName;
return base.Create(options, name, tenant, tenantHostName);
}
For tenant 2, it is setting proper API Name and Authority but still while validating the token API uses the API name of the first tenant.
I'm able to resolve it using following code.
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme,
jwtOptions =>
{
},
referenceOptions => {
});
And then registering the IOptionMonitor for JwtBearerOptions (for JWT Tokens) & OAuth2IntrospectionOptions (For Reference Tokens). I used both because my API support both type of tokens. (Reference & JWT). If you are using only one of the then you don't need to specify both.
services.AddSingleton<IOptionsMonitor<JwtBearerOptions>, JWTOptionsProvider>();
services.AddSingleton<IOptionsMonitor<OAuth2IntrospectionOptions>, OAuth2IntrospectionOptionsProvider>();
and you need to create to class "JWTOptionsProvider" inherit from TenantOptionsProvider & "OAuth2IntrospectionOptionsProvider" inherit from TenantOptionsProvider

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 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