Add a temporary claim From Account Controller Login Method - identityserver4

Using Identity Server Aspnet Identity Quickstart Link, with EntityFramework for Persistent Storage.
I added a dropdown list to login page along with username and password fields. The user is supposed to select a value from dropdown list while logging into the Identityserver.
How can I add the selected value of the dropdown list as a claim for that particular session and send it back to clients along with requested claims?
My clients are MVC application which communicates with Identity Server using Hybrid Flow.
IdentityServer v2.0.2
MVC Client : AspNet Core 2.0.0

Store the value in the signin cookie when calling SignInAsync
Return the value as a claim in the IProfileService

Related

IdentityServer4: How to set a role for Google user?

I have 3 applications:
An IdentityServer4 API which provides Google authentication and also provides an access token to authorize the resource API.
A simple Resource API which provides some data from DB.
A simple Client in React which have 4 buttons:
Login, for Google auth
Logout
Get data - a simple request with the access token to the Resource API and gets the data from Db
Get user data - returns user profile and token (for debug purpose)
I didn't put any sample code because my problem is not code related, it's knowledge that I'm missing and I ask for guidance.
The workflow is working just fine: the user press the Login button, it is redirected to IdentityServer4 API for Google Auth. From there it is redirected to a Callback Page from the Client and from there to the Index page. I receive the user data and the token, I can request data from the Resource API and it's working.
My problem is: How do I give a Role to the Google Users ?
I don't have users saved in DB. I want three types of Users: SuperAdmin, Admin, Viewer and each of these roles have limited Endpoints which can access.
For limiting their access I saw that I can use Claims-based authorization or Role-based authorization.
So, my question is how ca I give a Google User who wants to login in my app, a specific Claim/Role ? What is the workflow ? I must save it first in DB ? Or there exists a service from Google where I can add an email address and select a Role for that address ?
Thank you very much !
After you get the response from Google in your callback you can handle the user and do what ever you want to do with it. Below are the some typical tasks that you can do in callback that I took from documentation page of identityserver4 link:
Handling the callback and signing in the user
On the callback page your typical tasks are:
inspect the identity returned by the external provider.
make a decision how you want to deal with that user. This might be
different based on the fact if this is a new user or a returning
user.
new users might need additional steps and UI before they are allowed
in.
probably create a new internal user account that is linked to the
external provider.
store the external claims that you want to keep.
delete the temporary cookie
sign-in the user
What I would do is creating an new internal user account that is linked to the external provider and add a role to that user.
If you don't want to save users in db, you can add an extra claim to user in callback method and use that claim in token. and i think this link will help with that.

Azure AD Enterprise Application - Identify Caller Application

I have a REST API which uses Azure ADD App registration to allow other apps to call it.
In the Azure Portal, I have registered it as an Enterprise Application and also registered the consumer applications and assigned them Roles appropriately.
The authentication and RBAC works fine.
But the use case that I am working on requires me to identify and log the incoming request calling application's name (The one seen in the portal as 'Display Name', when we view the list of users and groups for an enterprise Application).
As advised in the internet, I am using some Identity related API to read the claims from the request header.
var provider = claimsUser.FindFirst("http://schemas.microsoft.com/identity/claims/identityprovider").Value;
var sid = claimsUser.FindFirst(ClaimTypes.NameIdentifier).Value;
OR
var sid = claimsUser.FindFirst("stable_sid").Value;
But this gives me a GUID value which I couldn't map to any of the consumers of the Enterprise Application.
The clients are all registered in the Azure portal.
In the Portal, I can see the apps in the "Users and Groups" section for the Enterprise application, with their appropriate roles.
In terms of usage, before making the call, the clients generate a bearer token based on the certificate that they get from Azure. The make the call with the bearer token attached to the request header. This bearer token is validated against the Azure AD, in the filters set before every controller..
What I want is to get are the details about this client who has made the call.. As per some repliers, and, to which I agree, the Guid that I get as part of the previous call mentioned above is for the actual user and not the app itself which is making the call.
Can anyone throw some light into it.. some code snippet will be of real help..
I'm not sure what authentication flow you are using, but SID is generally for a user that's logged in, not an application. if your client applications are using client id and secret, the token it returns that you send to the api should include the app registration guid. https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow#access-control-lists . So the appid and iss should give you the guid of the app registration. using this, you can make a call to graph api, to identify the display name of the app registration. https://learn.microsoft.com/en-us/graph/api/application-get?view=graph-rest-1.0&tabs=http
If your app is a user login app, and you don't want to make a call to graph, the other option you could do as a workaround would be to create app roles and assign users to them but name the app roles with some convention that includes the app's display name. then the name could come through under roles claim.. https://learn.microsoft.com/en-us/azure/active-directory/develop/howto-add-app-roles-in-azure-ad-apps
those are some options.. but other than calling graph or kinda working around to inject the name into a different claim of the token I'm not sure of any other method to get the "app registration's display name"

How to redirect to another application Single Sign On Identity Server 4 and logout

I have an application A and an application B both of which are Asp.Net MVC Core 2.2 which should behave as if they are a single application to the end user; I'm using SSO in Identity Server; and in order to share claims between them I have given them the same Authentication Cookie name, Application name and access to a shared data protection key location as per the instructions:
https://learn.microsoft.com/en-us/aspnet/core/security/cookie-sharing?view=aspnetcore-2.2
If I login to A and then Redirect to B I can see the claims as expected shared between the applications; however if I then logout from B the logoutId which is generated and sent to the Identity Server 4 has no knowledge of B.
If I give a different name for the Authentication Cookie then I cannot share the claims but after redirecting to B and then logout from B the logoutId which is generated works correctly.
If after redirecting to B then waiting for the login session to timeout and re-authenticating then logout the logout of B works correctly; and the logoutId which is generated and sent to Identity Server 4 has knowledge of both A and B.
I believe this is because after redirecting to B when the Authorize attribute triggers a check that the user is Authenticated; which the user is in Application A and this is 'mistaken' for being already Authenticated in B; as this is a SSO there is no reason to authenticate in B as if authentication was checked with the Identity Server 4 then the user would be authenticated; however since the authentication is not checked the knowledge that the user has redirected to B is not stored in the shared authentication cookie and thus on Sign-out the Identity Server 4 does not receive the correct logoutId.
What can I do to force Identity Server 4 to be notified and thus update the Shared Cookie after the redirect to Application B?
You should not use shared cookies between your Angular apps to achieve SSO behavior. Neither you should share claims between those apps. In OAuth, the SSO is achieved by having a single authority uri (your Identity Server 4) that all your domain apps use to authenticate users, meaning that once user goes to application A and is redirected to login and completes login flow, the cookie is left on the on your Identity Server 4 app, and then once you redirect your application B it will see that it's not authenticated and redirect to the Identity Server 4 which will then use the cookie and automatically log the user in (can even skip consent page optionally).
Single sign out is a bit more difficult and is achieved by using SignOutIFrame on your Identity Server 4. In a nutshell, the idea is that once you initiate the logout flow from one of your apps, after the user successfully logs out from the Identity Server 4 app, the rendering of this SignOutIFrame causes all other clients to optionally participate in the logout flow and clear the session, etc. for a given user. More info can be found here.

How to send email id when calling identity server 4 to login

I have a angular application and identity server 4 application. I have a login page in angular app which have only user name and when user enters his username , i want to send this username to IDs 4 and based on his username i will ask him to login either using password or redirect to AZure AD (i have saved these settings in DB which mode he needs to login).
From angular we are only able to call this method this.oidcSecurityService.authorize();
Can anyone suggest the way to send the username to IS4 from angular application using the angular-auth-oidc-client library.
OpenID Connect authorization requests can include the username in the login_hint parameter. IdentityServer will then make that available to you via the IIdentityServerInteractionService.
Your client library should allow you to set this query string parameter for authorization requests.

how to integrate regular username/password login with 3rd party social login for a Spring Boot + Angular single page web app?

I have a Angular + Spring boot single page web app. The server also acts as an Auth Server which issues tokens for the angular app to use to make Restful API calls.
My old login flow uses a grant_type=password POST call to the /oauth/token endpoint to get a Bearer token. And all further API calls on behalf of the user will include the Bearer token as the "Authorization" http header.
Now I need to integrate social login (facebook, twitter, etc.), which means I don't have username/password to generate tokens so I'm not sure how to make it work.
I have been using the following two tutorials as my template:
Spring Security and Angular JS
Spring Boot and OAuth
In the first tutorial's oauth-vanilla example, the username passwork login flow brings up the authorization page. But I'd like to have the traditional username/password form login experience (log user in directly instead of showing the Authorization page).
In the second tutorial, after facebook login, I'd like to use the facebook id to look up my internal user database and create a new user if not exist and logs him in as the user. And use the internal db user's identity and authorities to authorize future API calls to my API server.
I have a stripped down sample at at
https://github.com/dingquan/spring-angular-oauth
I can make POST calls to /oauth/token endpoint and use the returned token to make further api calls to my protected /api/blogs endpoint. But I haven't figure out how to make the following things work:
Username/password login that will create a session cookie so I don't need to send the Authorization bearer token for future API calls to the resource endpoint
After facebook login (the facebook login link is under the username/password login form), calls to my endpoint still fails with 401 error (I have a "test" button that makes a get call to /api/blogs, you can click on it to see the behavior). So what am I missing to make the API call succeed?
=== UPDATE ===
Just to clarify. Here are the goals I'm trying to achieve:
multiple ways of authentication (traditional username/password, third party oauth login such as facebook, possibly cellphone number + SMS code in the future)
we do need our own user model backed by DB to store other user attributes, pure social login is not enough
social login needs to be implicit. Meaning user should not be required to create a user account in our system manually once they login through a 3rd party (facebook, etc.). We're not just grabbing users' social profile data to pre-populate the registration form. We want to create new DB users automatically behind the scene if no existing db user is associated with the given external social account. i.e. if user is logged in through facebook, they don't need to enter username/password. Authentication through facebook will automatically log the user into our system as well and user should be able to access restricted resources after facebook login.
There's some confusion that I might be asking people to put their facebook username/password in a login form hosted by my app and I'll login facebook on behalf of the user. That's not what I was asking for.
You don't need such a complicated configuration. Add #EnableOAuth2Sso to your MainConfiguration and set appropriate application properties.
Here is what I have done in order to use Facebook as a authorization server.
a) Remove clientId and authServer from UserServiceImpl. Otherwise you'll be forced to configure an authorization server that is not needed.
b) Remove AuthorizationServerConfiguration completely.
c) Add #EnableWebSecurity and #EnableOAuth2Sso to your MainConfiguration.
d) Change MainConfiguration::configure to
http
.logout().logoutSuccessUrl("/").permitAll().and()
.authorizeRequests().antMatchers("/", "/login", "/home.html").permitAll()
.anyRequest().authenticated()
.and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
e) Delete everything else except nested class AuthenticationSecurity from MainConfiguration.
f) Change ResourceServerConfiguration::configure(HttpSecurity) to
http.antMatcher("/api/**").authorizeRequests().anyRequest().authenticated();
f) Remove attribute tokenStore and method ResourceServerConfiguration::configure(ResourceServerSecurityConfigurer) from ResourceServerConfiguration.
g) Remove configuration block security and facebook from application.yml. Instead add this
security:
oauth2:
client:
client-id: <CLIENT_ID>
token-name: oauth_token
authentication-scheme: query
client-authentication-scheme: form
access-token-uri: https://graph.facebook.com/oauth/access_token
user-authorization-uri: https://www.facebook.com/dialog/oauth
resource:
user-info-uri: https://graph.facebook.com/me
client-id: <CLIENT_ID>
client-secret: <CLIENT_SECRET>
token-type: code
h) In index.html change login to login.
i) Replace the content of hello.js with this one.
But I'd like to have the traditional username/password form login experience (log user in directly instead of showing the Authorization page).
I would never use a site that requires my credentials without redirecting me to the origin! I don't know you and you are under suspicion being a phishing site.
You should really reconsider your decision.
Btw, I created a pull request with these changes.

Resources