0

I am successfully authenticating the user at the Identity Server and getting back the claims (along with roles as claims). All is well when I do not consider roles. I want to restrict the user unless he has a specific role and redirect him to a specifically created "Unauthorised" or "Access denied" page. The following code below doesn't raise an unauthorised exception.

var canAccessPortal = id.HasClaim(c => c.Type == "role" && c.Value == "XP");
if (!canAccessPortal)
{
    throw new UnauthorizedAccessException();
}

Complete app.UseOpenIdConnectAuthentication code is below:

app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
    ClientId = ClientId,
    Authority = IdServBaseUri,
    RedirectUri = ClientUri,
    PostLogoutRedirectUri = ClientUri,
    ResponseType = "code id_token token",
    Scope = "openid profile roles clef",
    TokenValidationParameters = new TokenValidationParameters
    {
        NameClaimType = "name",
        RoleClaimType = "role"
    },
    SignInAsAuthenticationType = "Cookies",

    Notifications = new OpenIdConnectAuthenticationNotifications
    {
        AuthorizationCodeReceived = async n =>
        {
            // use the code to get the access and refresh token
            var tokenClient = new TokenClient(
                TokenEndpoint,
                ClientId,
                ClientSecret);

            var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(n.Code, n.RedirectUri);

            if (tokenResponse.IsError)
            {
                throw new Exception(tokenResponse.Error);
            }

            // use the access token to retrieve claims from userinfo
            var userInfoClient = new UserInfoClient(UserInfoEndpoint);

            var userInfoResponse = await userInfoClient.GetAsync(tokenResponse.AccessToken);

            // create new identity
            var id = new ClaimsIdentity(n.AuthenticationTicket.Identity.AuthenticationType);
            id.AddClaims(userInfoResponse.Claims);
            id.AddClaim(new Claim("access_token", tokenResponse.AccessToken));
            id.AddClaim(new Claim("expires_at", DateTime.Now.AddSeconds(tokenResponse.ExpiresIn).ToLocalTime().ToString()));
            id.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
            id.AddClaim(new Claim("sid", n.AuthenticationTicket.Identity.FindFirst("sid").Value));

            var canAccessPortal = id.HasClaim(c => c.Type == "role" && c.Value == "XP");
            if (!canAccessPortal)
            {
                throw new UnauthorizedAccessException();
            }

            n.AuthenticationTicket = new AuthenticationTicket(
                new ClaimsIdentity(id.Claims, n.AuthenticationTicket.Identity.AuthenticationType, "name", "role"),
                n.AuthenticationTicket.Properties);
        },

        RedirectToIdentityProvider = n =>
        {
            // if signing out, add the id_token_hint
            if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
            {
                var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token");

                if (idTokenHint != null)
                {
                    n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
                }
            }

            return Task.FromResult(0);
        }
    }
});

Custom error node in web.config file:

<customErrors mode="On" defaultRedirect="~/account/error">
  <error statusCode="401" redirect="~/account/unauthorized" />
</customErrors>
TejSoft
  • 3,213
  • 6
  • 34
  • 58

1 Answers1

0

The best place to implement authorization in the mvc pipeline is by implementing AuthorizationAttribute. Here you have full control over the request context. Make sure if you are authorizing for MVC you should implement System.Web.MVC .AuthorizeAttribute and implement override AuthorizeCore.

    protected override bool AuthorizeCore(HttpContextBase httpContext) {

        if (!base.AuthorizeCore(httpContext)) {
            return false;
        }
        //Check roles from DB service 
        return roleservice.UserInRole && UserClaims.Count > 0;
    }

    /// <summary>
    /// Processes HTTP requests that fail authorization.
    /// </summary>
    /// <param name="filterContext">Encapsulates the information for using <see cref="T:System.Web.Mvc.AuthorizeAttribute" />. The <paramref name="filterContext" /> object contains the controller, HTTP context, request context, action result, and route data.</param>
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) {
        filterContext.Result = new RedirectResult("~/main/unauthorized");
    }