Looking to implement IdentityServer to provide our systems with SSO.
All users, both internal to the organization and external would be stored in Active Directory. We would then use IdentityServer to authenticate against AD.
What we are looking to do is create buckets of claims for a user. So a user can have different values for their claims.
E.G
A development bucket may have the claim "My.Claim.Name" with a value of "Bob" and a production bucket may have the claim "My.Claim.name" with a value of "John"
Active directory isn't, as far as i am aware, able to silo attributes in this fashion so i would need to build this custom functionality into a Database that IdentityServer will access.
So that brings me to the question.
Is it possible to authenticate a user against one storage mechanism, but retrieve the claims for that user from another store? Essentially separating the user and their claims.
If so, can someone please point me in the direction of what interfaces may need to be implemented to make this happen?
IdentityServer implements the openid connect spec, which means only identity. Authorization should not be mixed with identity, ref this blog post by the authors https://leastprivilege.com/2016/12/16/identity-vs-permissions/
Authorization should be checked by each application (e.g. a user in API1 has access to these resource, the same user in API2 has access to other resources). This basically means that once authenticated, each application needs to get user authorization from a separate source (i.e your authorization store).
BUT, to answer your question, you should implement the IProfileService interface http://docs.identityserver.io/en/release/reference/profileservice.html?highlight=iprofileservice. This interface gets called when the user identity is created, letting you fill out claims based on requested scopes. Here you can fetch claims from your separate authorization database.
Note that this is considered not best practice, and the IdentityServer team has said they will come up with a proper Authorization solution some time soon.
Related
I'm adding an external identity provider, Azure multi-tenant, as a login option. I have local accounts right now and they know nothing about Azure AD users.
I want to create an Azure Enterprise App that other tenants can use to set up SSO using OpenID Connect. Also I want the app to support User Provisioning by setting up a SCIM API.
I can't really find any documents on how to handle linking Azure AD users with the existing accounts in my IDP. I've seen examples where users can login using their local account, authenticate with Azure, and then their local account is updated to have the Azure AD User ID. This approach still seems pretty manual. Another thought was to have a step in the login journey, where if no local account has the Azure AD User ID then find a local account with the same email. I don't like this approach either since the emails might not always match. Is there an approach where an admin can automatically link all accounts with a sync or upload?
SYSTEMS
First it's worth clarifying roles:
Role
Details
Authorization Server (AS)
Your UIs and APIs receive tokens from this. It is where accounts and linked accounts are stored. It is where you use SCIM to update account records. It is where you apply account linking logic.
Identity Provider (IDP)
There can be multiple of these. When your apps call the AS it manages connections to them, and exchanges IDP tokens for AS tokens that are returned to apps. It is not usual to use SCIM against an IDP.
You are using IdentityServer as the AS so your UIs and APIs will continue to use IdentityServer tokens and remain simple. Meanwhile Azure AD will become an alternative IDP. So on the data side of things your architecture is in a good place.
AUTHENTICATION ACTIONS
I would aim to solve your problems via custom authentication actions, and to design this in a vendor agnostic way. Not all providers support these concepts, but IdentityServer has some pretty good extensibility features, so hopefully it has what you need.
A bit of code, configuration and technical investigations in IdentityServer feel like the correct direction, with no complexity added to your applications. I will describe techniques in terms of Curity (where I work), but the same principles can apply to any system.
USERNAME AUTHENTICATOR
A great way to deal with user specific differences is to make the initial screen prompt only for an email. See this example for how that looks. Later, when authentication has completed, you could set a cookie so that this screen is not shown on subsequent logins.
Then, before asking for credentials, apply some scripted logic, eg to look up existing account attributes, and decide how the user should authenticate. You might decide to route unknown users to Azure AD or do something based on business partner email suffixes.
DATA UPDATES
Something simple that might work in advance of adding Azure AD support is to assign all users a Tenant ID, and perhaps existing users get a Tenant ID of 1. Only those users are allowed to sign in with Identity Server - all others have to use Azure AD.
SCRIPTED LOGIC AND ACCOUNT LINKING
For a worked example of how this looks, see this Account Linking with Facebook tutorial. In this example the objective is to update the main account with a new linked account. This account linking doc may give you some additional ideas for your scenario. It should be possible to run custom logic before triggering authentication or once your have the Azure IDP attributes.
INVOLVE THE USER IF NEEDED
It may also be useful to present a custom screen to ask the user if they have an existing account when they first login via Azure AD. If not then an Azure AD login can create the primary account in IdentityServer data in addition to a linked account.
USERS AND PARTNERS
How users onboard is interesting, and discussed in this detailed article. I always start by getting a feel for the type of assets involved:
Type
Description
Personal Assets
You allow any user to sign up and they only have access to their own assets
Corporate Assets
Users are provisioned by an administrator, eg a banker is granted access to financial data according to business rules
In your case it feels like users are from the second category, so to enable a user to fully sign up you need data from the partner, either fed in manually or by getting them to call your API, before you can assign the user the correct tenant ID. This might eventually result in TenantID=23, but there is nothing to stop you initially allowing users to onboard and placing them in a default TenantID=0 bucket.
Ultimately this all comes down to data, claims and API authorization. Eg APIs could return certain data to TenantID=0 users but only return privileged data to users whose tenant ID has been asserted by an administrator. Not sure if these thoughts of mine match your scenario but hopefully they provide a useful hint or two.
SUMMARY
Reliable account linking is tricky, but it should be solvable via the building blocks of the Authorization Server, using the above techniques. The end result should be deterministic user data, with no duplicate users.
I'm trying to configure Azure B2C in such a way that it prompts the user for a login id and password, these are then authenticated using an external REST call, and it returns an OIDC token containing whatever claims I wish to return in the REST call.
I can make the REST call work and add claims to an existing example policy no problem. The problem is removing the superfluous Azure AD behaviour without breaking the policy.
If the user doesn't exist in the Azure AD Directory, I don't care, I'm not interested in the user having an Azure profile at all, I want all the user data to come from the application's own database, but I haven't found a way to stop Azure from redirecting the user to create a new local AD account if one doesn't already exist.
The documentation is very dense and has a few walk-throughs of how to do a few of the things it expects you to want to do, but nothing like what I want to do. It points to a startup pack and some samples, but again these are very fully featured and not what I want to do, and trying to work backwards from them is proving intractible.
What's lacking is something that describes how to create a custom policy from scratch and take full control of the process of authenticating the user.
Does anyone have any suggestions of how to go about this, or know of some good documentation about this kind of thing?
There's an example here.
Basically, self-asserted screen to get credentials, verify against API, send JWT to application.
As shown by many samples I have two AAD application registrations, one for my javascript-based front end, and one for my JSON-only web APIs.
If I fully trust my client AAD application, why does AAD require me to create a second AAD application for my web APIs?
For example, assuming I add specific roles to my client AAD application, if client signs in with AAD and gets an id token and access token containing my roles, it only needs to send the access token to my APIs. The API only needs to crack the JWT, validate the audience, issuer, tenant, roles permissions, and signature. In this world, no client secret is needed in the web APIs, a second AAD application registration not needed, and still no call to AAD from my APIs. Unfortunately, without two AAD applications, I cannot figure out a way to have AAD include roles into my access token.
If I didn't fully trust the issuer from mucking with claims, I can see why I would need two AAD applications and a client secret. But since I do trust my AAD application and the signature of the JWT, why the extra complexity? Or maybe there is a way to do this that I haven't found?
Thanks!
Responding to Marc here because just not enough characters in the comments field -- The sample you referenced is an excellent sample, specifically the JavaScript one calling the Web API. It is what I am doing right now in fact. However, the problem is that Web API in the sample is open to anybody who has authenticated on the tenant. I need to secure the Web API down to certain individuals in the tenant, and simply checking the client/app id is not sufficient as anybody who can create an AAD app can fake it.
So what I need to do is have roles added to the the access token so I know that my application authenticated the user, and that user has been granted the required roles. For example, here is a Microsoft sample. And even here a Microsoft video walking through the process.
If I don't have two AAD applications w/client secret, the roles claims is never provided in the access token. It is always provided in the id token, but not the access token.
I feel like I am missing something obvious here. If AAD would just put the roles I requested into the JWT when I authenticated against it, and I validated its signature, audience, issuer, and roles, I wouldn't need any of this extra complexity?
Ah, I think I understand where you are going: you would like to control which users can access an API, no matter what client app they are using to access the API with. That's a function of the API - you cannot control that through AAD. In AAD you can control which users can access which applications (UI) using either user access restrictions (enterprise tab) or role-based access. However, access to an API is controlled in AAD at the calling application level via scopes. APIs are never accessed directly by users but only by other apps so controlling access permissions at user level would cause admin havoc. So, you can control what permissions a user has in the app they are using and you can control what permissions that application (client) has in other applications (APIs, resource servers) it is using.
In other words: role is about user access to UI, scope is about one apps' access to another.
App secrets provide added security for getting tokens - they have no bearing on what permissions are included in the token.
Can you provide a link showing that two apps are needed? That should only be the case if the API you want to call is not provided by the web app which served the JS to the browser. None of the 'official' samples require you to register two apps (Graph API, used in some of these samples is a separate API and it is already registered). A problem with tokens passed from the browser is that they were acquired by a public client, not using any secrets apart from user creds. Therefore, they are easier to steal and re-use. Your own back-end app may want to use a secret to get its own token (extension grant) to call yet another API using a token that does not reside in a public client.
I'm struggling with the correct way to secure a multi-tenant Web API with Identity Server. Let me explain.
We have a multi-tenant Web API that serves a ASP.NET MVC application.
Each new customer is assigned a new TenantId.
A customer can have multiple subscriptions of the application. Its
the same as saying that the app manages multiple databases per
customer (that he can access from the same base URL).
Each user belongs to a single customer (tenant) and will have access
to all that customer's subscriptions.
The API is set in a way that every endpoint includes both the tenant id and the subscription id so it can know from which subscription/database it should get the data.:
<server>/tentantId/subscriptionId/(...)
Now imagine that I have another external app (say a console app), using the client credentials flow, that is trying to access some API resource "on behalf" of a customer, meaning that will use a specific tenantId/subscriptionId pair:
<server>/1000/1/products
Every time a call hits one of the API endpoints I need to validate that this specific client app can access that tenant/subscription.
It would make a lot of sense if the Identity Server could perform that check automatically as part of the authorization flow.
If we added some way for the customer to register (consent) a specific client app to access the Web API on it's subscriptions, may be we could also set the Identity Server to know that in the form of scopes or at least include that information in the claims so that we could perform the permission check by inspecting the token instead of calling an external component.
Is this even possible?
Should I try to use scopes? Claims?
Can anyone point me in the right direction?
Your question is confusing when you talk about multi-tenancy. Isn't an API multi-tenant by default? The way I see it, it's a resource that can be accessed by multiple users / clients.
If I understand correctly, all you want is to access the API through the MVC app on behalf of a user. In other words: a hybrid flow with API access.
Instead of putting the userid in the path, use the id from the sub claim. Which lets the API distinguish between calls on behalf of clients and calls on behalf of users.
The resource should take care of authorization. Depending on the type of authorization you can use claims. If a subscription needs to be checked then this should be done by the API, using the sub claim to distinguish the user.
Scopes on the other hand are meant to define the resource. With the scope "api1" I can access the Api1 resource (api). But it says nothing about authorization.
IdentityServer provides Authentication as a Service.
It is your apps' duty to provide the actual Authorization.
What is the correct way to provide users with programmatic access to APIs?
I have an application composed of many services (all trusted services, within the same domain, not external applications), and identity service in the same domain.
I would like to allow a user to user a set of credentials to gain access to any of the APIs I provide.
Is it reasonable to think of a user as a client in this case, and use clientCredentials flow to get a token that will be passed to the target API? specifically, is it a good idea automatically assign a clientId and a clientSecret to every user in my system, and allow all users to use clientCredentials flow to request an access token? the access token would then contain all user's claims. If this is a reasonable approach, is there any way to remove the "client_" prefix from the added claims? or is that a bad idea?
This was originally asked here: https://github.com/IdentityServer/IdentityServer4/issues/1877