User Service

The sample for this topic can be found here

IdentityServer3 defines the IUserService interface to abstract the underlying identity management system being used for users. It provides semantics for users to authenticate with local accounts as well as external accounts. It also provides identity and claims to IdentityServer needed for tokens and the user info endpoint. Additionally, the user service can control the workflow a user will experience at login time before being allowed to return to the client application (e.g. for things such as additional login requirements such as 2fa or other custom requirements such as accepting a EULA).

The methods on the user service are broken down into methods that relate to authentication and methods that relate to the user’s profile and issuing claims for tokens.

IUserService

The IUserService interface defines these methods:

SignInMessage

The authentication methods all receive a SignInMessage in their context which contains these properties:

AuthenticateResult

The return value of all of the authentication methods is an AuthenticateResult. The returned AuthenticateResult indicates one of many possible outcomes. The constructor is overloaded and the one used indicates which of these outcomes is chosen. The list is:

// Full login
AuthenticateResult(string subject, string name, IEnumerable<Claim> claims = null, string identityProvider = Constants.BuiltInIdentityProvider, string authenticationMethod = null)

// Partial Login (where subject is known)
AuthenticateResult(string redirectPath, string subject, string name, IEnumerable<Claim> claims = null, string identityProvider = Constants.BuiltInIdentityProvider, string authenticationMethod = null)

// Partial Login (where subject is not known)
AuthenticateResult(string redirectPath, IEnumerable<Claim> claims)

// Partial Login (from external login)
AuthenticateResult(string redirectPath, ExternalIdentity externalId)

// Login error
AuthenticateResult(string errorMessage)

Full login

To fully log the user in the authentication API must produce a subject and a name that represent the user. The subject is the user service’s unique identifier for the user and the name is a display name for the user that will be displayed in the user interface.

Optionally a list of Claim can also be provided. These claims can be any additional values that might be needed by the user service in the other APIs on the user service.

If the user is being logged in and an external identity provider was used, then the identityProvider parameter should also be specified. This will be included as the idp claim in the identity token and the user info endpoint. If the user is being authenticated with a local account, then this parameter should not be used (and let the default of Constants.BuiltInIdentityProvider be used instead).

There is also an optional authenticationMethod parameter which populates the amr claim. This can be used to indicate how the user authenticated, such as two factor authentication, or client certificates. If it is not passed, then password is assumed for local logins. For external logins, then the value of external will automatically be used to indicate an external authentication.

The entirety of these claims (subject, name, idp, amr and the optional list of Claim) are used to populate the authentication cookie that is issued for the user for IdentityServer. This authentication cookie is issued and managed by using the Katana cookie authentication middleware with an AuthenticationType indicated by the constant Constants.PrimaryAuthenticationType.

The ClaimsPrincipal that is created from the full login is then used as the Subject for the other APIs on the IUserService. These APIs are PostAuthenticateAsync, GetProfileDataAsync, IsActiveAsync, and SignOutAsync.

Partial login

In addition to a full login, the authentication APIs can perform a “partial login”. A partial login allows the user service to interrupt the user’s login workflow and redirect them to a custom page where they must perform some action before they can continue to login (e.g. performing 2fa, completing a registration form, or accepting a EULA).

This partial login is performed by issuing a “partial login” cookie using the Katana cookie authentication middleware with an AuthenticationType indicated by the constant Constants.PartialSignInAuthenticationType.

The partial login can be created with all of the same parameters as described above for a full login (i.e. subject, name, claims, amr, and idp) as well as a redirectPath. Alternatively, the partial login can be created with just a claims collection as well as a redirectPath. The main difference is if the user’s identity (subject) has been determined.

The redirectPath represents a custom web page provided by the hosting application that the user will be redirected to. On that web page the user’s claims can be used to complete the custom workflow. In order to obtain these claims, the page can use the GetIdentityServerPartialLoginAsync OWIN environment extension method.

Once the user has completed their work on the custom web page, they can be redirected back to IdentityServer to continue with the full login process. The URL to redirect the user back to can be obtained via the GetPartialLoginResumeUrlAsync OWIN environment extension method. If the page needs to send the user back to the login page, then the GetPartialLoginRestartUrlAsync OWIN environment extension method can be used instead.

Before redirecting the user back into IdentityServer, the claims in the partial login can be changed with the UpdatePartialLoginClaimsAsync OWIN environment extension method. If the page needs to remove or clear the partial login, then the RemovePartialLoginCookie OWIN environment extension method can be used.

Partial Login (from external login)

It’s possible that the user has performed an external authentication, but there is no local account for the user. A custom user service can choose to redirect the user without a local account. This is performed by creating a AuthenticateResult with a redirect and the ExternalIdentity passed to the AuthenticateExternalAsync API. This performs a partial login (as above via the PartialSignInAuthenticationType) but there is no subject claim in the issued cookie. Instead there is a claim of type external_provider_user_id (or via Constants.ClaimTypes.ExternalProviderUserId) whose Issuer is the external provider identifier and whose value is the external provider’s identifier for the user. These values can then be used to create a local account and associate the external account.

Once the user has completed their registration on the custom web page, they can be redirected back to IdentityServer via the same mechanisms as described above.

Login error

Finally, the authentication APIs can provide an error that will be displayed on the login view. This is indicated by creating the AuthenticateResult using the constructor that accepts a string (the error) as its argument.