Quantcast
Channel: damienbod – Software Engineering
Viewing all articles
Browse latest Browse all 357

Full Server logout with IdentityServer4 and OpenID Connect Implicit Flow

$
0
0

The article shows how to fully logout from IdentityServer4 using an OpenID Connect Implicit Flow. Per design when using an access token to use protected data from a resource server, even if the client has logged out from the server, the access token can be used so long it is valid (AccessTokenLifetime) as it is a consent. This it the normal use case.

Sometimes, it is required that once a user logs out from IdentityServer4, no client with the same user can continue to use the protected data without logging in again. Reference tokens can be used to implement this. With reference tokens, you have full control over the lifecycle.

Code: https://github.com/damienbod/AspNet5IdentityServerAngularImplicitFlow

Other posts in this series:

To use reference tokens in IdentityServer4, the client can be defined with the AccessTokenType property set to AccessTokenType.Reference. When a user and the client successfully login, a reference token as well as an id_token is returned to the client and not an access token and an id_token. (response_type: id_token token)

public static IEnumerable<Client> GetClients()
{
	// client credentials client
	return new List<Client>
	{
		new Client
		{
			ClientName = "angular2client",
			ClientId = "angular2client",
			AccessTokenType = AccessTokenType.Reference,
			AllowedGrantTypes = GrantTypes.Implicit,
			AllowAccessTokensViaBrowser = true,
			RedirectUris = new List<string>
			{
				"https://localhost:44311"

			},
			PostLogoutRedirectUris = new List<string>
			{
				"https://localhost:44311/Unauthorized"
			},
			AllowedCorsOrigins = new List<string>
			{
				"https://localhost:44311",
				"http://localhost:44311"
			},
			AllowedScopes = new List<string>
			{
				"openid",
				"dataEventRecords",
				"securedFiles"
			}
		}
	};
}

In IdentityServer4, when a user decides to logout, the IPersistedGrantService can be used to remove reference tokens for this user and client. The RemoveAllGrantsAsync method from the IPersistedGrantService uses the Identity subject and the client id to delete all of the corresponding grants. The GetSubjectId method is an IdentityServer4 extension method for the Identity. The HttpContext.User can be used to get this. The client id must match the client from the configuration.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.Extensions.Logging;
using IdentityServerWithAspNetIdentity.Models;
using IdentityServerWithAspNetIdentity.Models.AccountViewModels;
using IdentityServerWithAspNetIdentity.Services;
using IdentityServer4.Services;
using IdentityServer4.Quickstart.UI.Models;
using Microsoft.AspNetCore.Http.Authentication;
using IdentityServer4.Extensions;

namespace IdentityServerWithAspNetIdentity.Controllers
{
    [Authorize]
    public class AccountController : Controller
    {
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly SignInManager<ApplicationUser> _signInManager;
        private readonly IEmailSender _emailSender;
        private readonly ISmsSender _smsSender;
        private readonly ILogger _logger;
        private readonly IIdentityServerInteractionService _interaction;
        private readonly IPersistedGrantService _persistedGrantService;

        public AccountController(
            IIdentityServerInteractionService interaction,
            IPersistedGrantService persistedGrantService,
            UserManager<ApplicationUser> userManager,
            SignInManager<ApplicationUser> signInManager,
            IEmailSender emailSender,
            ISmsSender smsSender,
            ILoggerFactory loggerFactory)
        {
            _interaction = interaction;
            _persistedGrantService = persistedGrantService;
            _userManager = userManager;
            _signInManager = signInManager;
            _emailSender = emailSender;
            _smsSender = smsSender;
            _logger = loggerFactory.CreateLogger<AccountController>();
        }

        /// <summary>
        /// Handle logout page postback
        /// </summary>
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Logout(LogoutViewModel model)
        {
            var subjectId = HttpContext.User.Identity.GetSubjectId();

            // delete authentication cookie
            await HttpContext.Authentication.SignOutAsync();


            // set this so UI rendering sees an anonymous user
            HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity());

            // get context information (client name, post logout redirect URI and iframe for federated signout)
            var logout = await _interaction.GetLogoutContextAsync(model.LogoutId);

            var vm = new LoggedOutViewModel
            {
                PostLogoutRedirectUri = logout?.PostLogoutRedirectUri,
                ClientName = logout?.ClientId,
                SignOutIframeUrl = logout?.SignOutIFrameUrl
            };


            await _persistedGrantService.RemoveAllGrantsAsync(subjectId, "angular2client");

            return View("LoggedOut", vm);
        }

The IdentityServer4.AccessTokenValidation NuGet package is used on the resource server to validate the reference token sent from the client. The IdentityServerAuthenticationOptions options are configured as required.

"IdentityServer4.AccessTokenValidation": "1.0.1-rc1"

This package is configured in the Startup class in the Configure method.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
	loggerFactory.AddConsole();
	loggerFactory.AddDebug();

	app.UseExceptionHandler("/Home/Error");
	app.UseCors("corsGlobalPolicy");
	app.UseStaticFiles();

	JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

	IdentityServerAuthenticationOptions identityServerValidationOptions = new IdentityServerAuthenticationOptions
	{
		Authority = "https://localhost:44318/",
		ScopeName = "dataEventRecords",
		ScopeSecret = "dataEventRecordsSecret",
		AutomaticAuthenticate = true,
		SupportedTokens = SupportedTokens.Both,
		// required if you want to return a 403 and not a 401 for forbidden responses

		AutomaticChallenge = true,
	};

	app.UseIdentityServerAuthentication(identityServerValidationOptions);

	app.UseMvc(routes =>
	{
		routes.MapRoute(
			name: "default",
			template: "{controller=Home}/{action=Index}/{id?}");
	});
}

The SPA client can then be used to login, logout from the server. If 2 or more clients with the same user are logged in, once the user logs out from the server, none will have access to the protected data. All existing reference tokens for this user and client can no longer be used to access the protected data.

By using reference tokens, you have full control over the access lifecycle to the protected data. Caution should be taken when using long running access tokens.

Another strategy would be to use short lived access tokens and make the client refresh this regularly. This reduces to time which an access token lives after a logout, but the access token can still be used to access the private data until it has timed out.

Links

http://openid.net/specs/openid-connect-core-1_0.html

http://openid.net/specs/openid-connect-implicit-1_0.html

https://github.com/IdentityServer/IdentityServer4/issues/313#issuecomment-247589782

https://github.com/IdentityServer/IdentityServer4

https://leastprivilege.com

https://github.com/IdentityServer/IdentityServer4/issues/313

https://github.com/IdentityServer/IdentityServer4/issues/310



Viewing all articles
Browse latest Browse all 357

Trending Articles