Skip to content
This repository has been archived by the owner on Dec 24, 2020. It is now read-only.

How can handle Authorize attribute on ASOS #536

Closed
p30help opened this issue Dec 12, 2018 · 6 comments
Closed

How can handle Authorize attribute on ASOS #536

p30help opened this issue Dec 12, 2018 · 6 comments
Labels

Comments

@p30help
Copy link

p30help commented Dec 12, 2018

Hi
I use ROPC flow on my project, also i use .Net Core v2.1
so i added this codes to ConfigureService on Startup.cs

        services.AddAuthentication().AddOpenIdConnectServer(options =>
        {
             options.AllowInsecureHttp = true;
             options.ApplicationCanDisplayErrors = true;

             options.UseSlidingExpiration = true;

             options.TokenEndpointPath = "/token";
             options.ProviderType = typeof(OAuthAppProvider);

             options.AccessTokenLifetime = TimeSpan.FromHours(24);
        });

then implement OpenIdConnectServerProvider class like this:

public sealed class OAuthAppProvider : OpenIdConnectServerProvider {
        private ILogsFacade _fcLog;
        private IUsersFacade _fcUsers;

        public OAuthAppProvider(IUsersFacade fcUsers, ILogsFacade fcLog)
        {
            _fcLog = fcLog;
            _fcUsers = fcUsers;
        }

        public override Task ValidateTokenRequest(ValidateTokenRequestContext context)
        {
            // Reject token requests that don't use grant_type=password or grant_type=refresh_token.
            if (!context.Request.IsPasswordGrantType() && !context.Request.IsRefreshTokenGrantType())
            {
                context.Reject(
                    error: OpenIdConnectConstants.Errors.UnsupportedGrantType,
                    description: "Only grant_type=password and refresh_token " +
                                 "requests are accepted by this server.");

                return Task.CompletedTask;
            }

            //check user & password is not empty
            if (context.Request.IsPasswordGrantType() && (
                context.Request.Username.IsNullOrWhiteSpace() ||
                context.Request.Password.IsNullOrWhiteSpace()
                ))
            {
                context.Reject(
                    error: OpenIdConnectConstants.Errors.UnsupportedGrantType,
                    description: "Only grant_type=password and refresh_token " +
                                 "requests are accepted by this server.");

                return Task.CompletedTask;
            }

            context.Skip();

            return Task.CompletedTask;
        }

        public override Task HandleTokenRequest(HandleTokenRequestContext context)
        {
            if (!context.Request.IsPasswordGrantType())
            {
                return Task.CompletedTask;
            }
         
            try
            {

                Thread.Sleep(1000);

                var user = _fcUsers.GetByUserPassNoHash(context.Request.Username, context.Request.Password); 

                if (user == null)
                {
                    Thread.Sleep(1000);
                    context.Reject(
                        error: OpenIdConnectConstants.Errors.InvalidGrant,
                        description: Resources.Security.UsernameOrPasswordIsIncorrect);

                    return Task.CompletedTask;
                }

                if (user.IsEnabled == false)
                {
                    Thread.Sleep(1000);
                    context.Reject(
                        error: OpenIdConnectConstants.Errors.InvalidGrant,
                        description: Resources.Security.UserHasBeenDisabled);

                    return Task.CompletedTask;
                }

                var identity = prepareClaimIdentity(context.Scheme.Name, user);

                // Create a new authentication ticket holding the user identity.
                var ticket = new AuthenticationTicket(
                    new ClaimsPrincipal(identity),
                    new AuthenticationProperties(),
                    OpenIdConnectServerDefaults.AuthenticationScheme //you can use context.Scheme.Name instead this line
                    );

                // (specify offline_access to issue a refresh token).
                ticket.SetScopes(OpenIdConnectConstants.Scopes.OfflineAccess);

                context.Validate(ticket);
            }
            catch (Exception exp)
            {
                Console.WriteLine(exp);
                throw;
            }

            return Task.CompletedTask;
        }

        private ClaimsIdentity prepareClaimIdentity(string authenticationType,  Security.DataModel.User user)
        {
            //var identity = new ClaimsIdentity(authenticationType);
            var identity = new ClaimsIdentity(authenticationType,
                OpenIdConnectConstants.Claims.Name,
                OpenIdConnectConstants.Claims.Role);

            // Add the mandatory subject/user identifier claim.
            identity.AddClaim(OpenIdConnectConstants.Claims.Subject, "[unique id]");

            identity.AddClaim("urn:customclaim", "value",
                OpenIdConnectConstants.Destinations.AccessToken,
                OpenIdConnectConstants.Destinations.IdentityToken);

            List<string> roles = new List<string>();
            roles.AddRange(user.UserRole.GetApiRoles());
            roles.AddRange(user.GetApiRoles());
            foreach (var apiRole in roles.Distinct())
            {
                identity.AddClaim(new Claim(ClaimTypes.Role, apiRole));
            }
            identity.AddClaim(new Claim(ClaimTypes.Name, user.SpecificName));
            identity.AddClaim(new Claim(Security.Consts.UserNameCliamName, user.UserName));
            identity.AddClaim(new Claim(Security.Consts.UserIdCliamName, user.Id.ToString()));

            return identity;
        }

}

and can get access_token
then for test it i create a controller like this :

    public class UserController : Controller
    {
        [Route("~/Test")]
        [HttpPost]
        [Authorize]
        public IActionResult Test()
        {
            return Content("Hello");
        }
     }

but how can handle authorize attribute because after call this action i received under error:

InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found.

i search this error and find some result like this:
https://stackoverflow.com/questions/52287542/invalidoperationexception-no-authenticationscheme-was-specified-and-there-was
https://stackoverflow.com/questions/47324129/no-authenticationscheme-was-specified-and-there-was-no-defaultchallengescheme-f/47622172

but none of them is not compatible with ASOS :(
also i checked HttpContext.User.Identity.IsAuthenticated that is always false(even when i send Authorization on header request)

how can handle Authorize Attribute for ASOS?

@kevinchalet
Copy link
Member

how can handle Authorize Attribute for ASOS?

ASOS doesn't validate tokens, it only issues them. Instead, you'll want to use the OAuth validation handler and register it using services.AddAuthentication().AddOAuthValidation(). Its default authentication scheme is "Bearer", for which you can find a constant in OAuthValidationDefaults.

Cheers.

@p30help
Copy link
Author

p30help commented Dec 14, 2018

Thanks a lot
I added services.AddAuthentication().AddOAuthValidation() but AddOAuthValidation() not found.
I use "OpenIdConnectServer" package.
do i need to add "OpenIddict 2.x" package too?

@kevinchalet
Copy link
Member

No, you just need the AspNet.Security.OAuth.Validation package (which is also what the OpenIddict validation module uses under the hood).

@p30help
Copy link
Author

p30help commented Dec 15, 2018

Thanks a lot again
I could validate access_token now,
Another small problem:
Custom claims not generated on access_token, for example i added some claims to "identity" object and then call Validate(); method
claim result is like under:

{sub: [unique id]}
{username: admin}
{scope: offline_access}

but when validate access_token and get claims from context.Principal.Claims some of custom claim is not existed, like this:

{sub: [unique id]}
{scope: offline_access}

why my custom claims not generated on access_token at first?

@kevinchalet
Copy link
Member

@p30help
Copy link
Author

p30help commented Dec 17, 2018

God bless you 👍

@p30help p30help closed this as completed Dec 17, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Development

No branches or pull requests

2 participants