mattbrailsford / umbraco-authu

An Umbraco plugin to add an OAuth API endpoint to allow authenticated Members/Users via OAuth
MIT License
71 stars 22 forks source link

The [Authorize] always caused "Authorization has been denied for this request" #13

Closed biapar closed 6 years ago

biapar commented 7 years ago

Hi,

I'm able to login and receive token. So I create a class like your webapi example, but with Authorize decoration, I've always the error "Authorization has been denied". If I remove the [Authorize], I'm able to call the webapi. Why?

The code is:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Umbraco.Web.WebApi;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Cors;
using LGISuppliersWFRepo;

using Our.Umbraco.AuthU;
using Our.Umbraco.AuthU.Services;
using Our.Umbraco.AuthU.Data;
using Our.Umbraco.AuthU.Extensions;

namespace LGISuppliersWFBusiness.Controllers
{
    [Our.Umbraco.AuthU.Web.WebApi.OAuth("realmusers")]
    public class LGIWFPraticheController : UmbracoApiController
    {
        [HttpGet]
        [Authorize]
        [EnableCors("*", "*", "*")]
        public string  HelloWorld()
        {
            return "Hello " + Members.GetCurrentMember()?.Name;
        }
    }
}
mattbrailsford commented 7 years ago

Since you have declared a realm in the OAuth attribute, have you also declared the same realm in your OAuth configuration in the application event handler?

biapar commented 7 years ago

Yes. I had. This is the configuration:

protected override void ApplicationStarted(UmbracoApplicationBase umbraco, ApplicationContext context)
    {

        // Gets a reference to the section (if already added)
        Section section = context.Services.SectionService.GetByAlias(SkriftSectionAlias);

        //if (section != null)
        //    return;

        // Add a new "Skrift Demo" section
        context.Services.SectionService.MakeNew("LGI Zone", SkriftSectionAlias, "icon-newspaper");

        // Grant all existing users access to the new section
        context.Services.UserService.AddSectionToAllUsers(SkriftSectionAlias);

        OAuth.ConfigureEndpoint("realm", "/oauth/token", new OAuthOptions
        {
            UserService = new UmbracoMembersOAuthUserService(),
            SymmetricKey = "856FECBA3B06519C8DDDBC80BB080553",
            AccessTokenLifeTime = 20, // Minutes
            ClientStore = new UmbracoDbOAuthClientStore(),
            RefreshTokenStore = new UmbracoDbOAuthRefreshTokenStore(),
            RefreshTokenLifeTime = 1440, // Minutes (1 day)
            AllowedOrigin = "*",

            AllowInsecureHttp = true // During development only
        });

        OAuth.ConfigureEndpoint("realmusers", "/oauth/tokenusers", new OAuthOptions
        {
            UserService = new UmbracoUsersOAuthUserService(),
            SymmetricKey = "856FECBA3B06519C8DDDBC80BB080888",
            AccessTokenLifeTime = 20, // Minutes
            ClientStore = new UmbracoDbOAuthClientStore(),
            RefreshTokenStore = new UmbracoDbOAuthRefreshTokenStore(),
            RefreshTokenLifeTime = 1440, // Minutes (1 day)
            AllowedOrigin = "*",
            AllowInsecureHttp = true // During development only
        });

    }
mattbrailsford commented 7 years ago

Ok, can you inspect the body of the request to see if there is a payload. If it's coming from AuthU, it should give you a message stating exactly what is causing it.

biapar commented 7 years ago

Here is the info;

image

Response: image

mattbrailsford commented 7 years ago

Hmm, that doesn't look to be one of our error messages so must be coming from some place else?

biapar commented 7 years ago

I tried to debug your code and I put some breakpoints into it. I see , the breakpoints aren't reached when I call the webapi but only when I call the token request .With / Without the "decoration" this breakpoint is called: public async Task ExecuteAsync(CancellationToken cancellationToken).

So the denied authorization is caused by UmbracoApiController. Do you agree?

mattbrailsford commented 7 years ago

Hmm, possibly. Have you put the token paths in the umbraco reserved paths app setting?

biapar commented 7 years ago

Yes... <add key="umbracoReservedPaths" value="~/umbraco,~/install/,~/oauth/token,~/oauth/tokenusers,~/oauth/" />

I've Umbraco Identity installed but not Google&Co activacted. I need it because I wish to login with Active Directory.

mattbrailsford commented 7 years ago

Have you tried debugging into the OAuthAttribute to see if that gets hit?

biapar commented 7 years ago

OAuthAttribute is not called. I putted breaks into it.

mattbrailsford commented 7 years ago

And you are sure that's the right one? there are two attributes. One for web API one for normal mvc controllers.

biapar commented 7 years ago

I try with also with Umbraco.Web.WebApi.UmbracoAuthorize

biapar commented 7 years ago

If you see the code, I don't use the MVC namespace as reported here https://docs.microsoft.com/en-us/aspnet/web-api/overview/security/authentication-and-authorization-in-aspnet-web-api

biapar commented 7 years ago

I found that username is null here:

image

mattbrailsford commented 7 years ago

Interesting. Have you tried decoding your JWT access token to see what it actually contains? You can do that here https://jwt.io/

biapar commented 7 years ago

Decoded token:

image

biapar commented 7 years ago

the code is not able to decode the token. See here:

image

biapar commented 7 years ago

The claims list is empty

image

mattbrailsford commented 7 years ago

That fact that jwt.io reported invalid signature is not a problem, it's just it doesn't know the server signing key so it can't validate that, but it still shows you the payload.

Looking at your payload though it doesn't look quite right. This JWT token came from the OAuth endpoint right? And are you using the out of the box user service? I'm not quite sure why some keys are missing. What version of umbraco are you using?

mattbrailsford commented 7 years ago

It could be worth debugging into the user service when the jwt token is being created as it looks like some things are not getting into the token.

biapar commented 7 years ago

I use Umbraco 7.5.13. The JWT token come from your OAuth endpoint and I use Umbraco Identity manager. Which other debug can I

<add key="owin:appStartup" value="UmbracoIdentityStartup" /> `using System; using System.Collections.Generic; using System.Linq; using System.Web; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.Owin; using Microsoft.Owin; using Microsoft.Owin.Security.Cookies; using UmbracoIdentity; using LGISuppliersWF.Models.UmbracoIdentity; using LGISuppliersWF; using Owin; using Umbraco.Web; using Umbraco.Web.Security.Identity;

using UmbracoIdentity.Models; using Umbraco.Core.Security; using Umbraco.Core.Models.Identity; using Umbraco.IdentityExtensions; using Microsoft.Owin.Cors; using System.Web.Cors; using LGISuppliersWFBusiness.Common;

[assembly: OwinStartup("UmbracoIdentityStartup", typeof(UmbracoIdentityStartup))]

namespace LGISuppliersWF {

/// <summary>
/// OWIN Startup class for UmbracoIdentity 
/// </summary>
public class UmbracoIdentityStartup : UmbracoDefaultOwinStartup
{
    /// <summary>
    /// Configures services to be created in the OWIN context (CreatePerOwinContext)
    /// </summary>
    /// <param name="app"/>
    protected override void ConfigureServices(IAppBuilder app)
    {

        //app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

        base.ConfigureServices(app);

        //Single method to configure the Identity user manager for use with Umbraco
        app.ConfigureUserManagerForUmbracoMembers<UmbracoApplicationMember>();

        //Single method to configure the Identity user manager for use with Umbraco
        app.ConfigureRoleManagerForUmbracoMembers<UmbracoApplicationRole>();

        //app.UseUmbracoBackOfficeTokenAuth();

        //app.UseUmbracoBackOfficeTokenAuth(new BackOfficeAuthServerProviderOptions());

        //app.UseUmbracoBackOfficeTokenAuth(
        //new BackOfficeAuthServerProviderOptions()
        //{

        //    CorsPolicy = new CorsPolicy()
        //        {
        //            AllowAnyHeader = true,
        //            AllowAnyMethod = true,
        //            AllowAnyOrigin = true,

        //        }

        //});

        // Configure my active directory password checker. Biagio

        //app.ConfigureUserManagerForUmbracoBackOffice<BackOfficeUserManager, BackOfficeIdentityUser>(
        //    ApplicationContext,
        //    (options, context) =>
        //    {
        //        var membershipProvider = MembershipProviderExtensions.GetUsersMembershipProvider().AsUmbracoMembershipProvider();
        //        var userManager = BackOfficeUserManager.Create(options,
        //            ApplicationContext.Services.UserService,
        //            ApplicationContext.Services.ExternalLoginService,
        //            membershipProvider);

        //        // Call custom passowrd checker.
        //        userManager.BackOfficeUserPasswordChecker = new BackofficeMembershipProviderPasswordChecker();

        //        return userManager;
        //    });

    }

    /// <summary>
    /// Configures middleware to be used (i.e. app.Use...)
    /// </summary>
    /// <param name="app"/>
    protected override void ConfigureMiddleware(IAppBuilder app)
    {
        //Ensure owin is configured for Umbraco back office authentication. If you have any front-end OWIN
        // cookie configuration, this must be declared after it.
        app
            .UseUmbracoBackOfficeCookieAuthentication(ApplicationContext, PipelineStage.Authenticate)
            .UseUmbracoBackOfficeExternalCookieAuthentication(ApplicationContext, PipelineStage.Authenticate);

        // Enable the application to use a cookie to store information for the 
        // signed in user and to use a cookie to temporarily store information 
        // about a user logging in with a third party login provider 
        // Configure the sign in cookie
        app.UseCookieAuthentication(new FrontEndCookieAuthenticationOptions
        {
            Provider = new CookieAuthenticationProvider
            {
                // Enables the application to validate the security stamp when the user 
                // logs in. This is a security feature which is used when you 
                // change a password or add an external login to your account.  
                OnValidateIdentity = SecurityStampValidator
                    .OnValidateIdentity<UmbracoMembersUserManager<UmbracoApplicationMember>, UmbracoApplicationMember, int>(
                        TimeSpan.FromMinutes(30),
                        (manager, user) => user.GenerateUserIdentityAsync(manager),
                        UmbracoIdentity.IdentityExtensions.GetUserId<int>)
            }
        }, PipelineStage.Authenticate);

        // Uncomment the following lines to enable logging in with third party login providers

        //app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

        //app.UseMicrosoftAccountAuthentication(
        //  clientId: "",
        //  clientSecret: "");

        //app.UseTwitterAuthentication(
        //  consumerKey: "",
        //  consumerSecret: "");

        //app.UseFacebookAuthentication(
        //  appId: "",
        //  appSecret: "");

        //app.UseGoogleAuthentication(
        //  clientId: "",
        //  clientSecret: ""); 

        //Lasty we need to ensure that the preview Middleware is registered, this must come after
        // all of the authentication middleware:
        app.UseUmbracoPreviewAuthentication(ApplicationContext, PipelineStage.Authorize);

    }

}

}`

mattbrailsford commented 7 years ago

Hmmm, there isn't actually a need to install the UmbracoIdentity package so I wonder if that is actually messing it up. AuthU should be working directly with the Users membership provider.

biapar commented 7 years ago

I update to U7.6.3 and disabled Identity. Now, the url http://localhost:60936/oauth/tokenusers is no longer available. I see that the normal Umbraco Backoffice Login search the realm endpoint? Why?

This is the error 500 from http://localhost:60936/umbraco/backoffice/UmbracoApi/Authentication/PostLogin:

And endpoint for the realm "realmusers" has not yet been configured. Please call ConfigureEndpoint first. System.Exception in Our.Umbraco.AuthU.OAuth.GetContext(String realm) in C:\projects\umbraco-authu\src\Our.Umbraco.AuthU\OAuth.cs:riga 24 in Our.Umbraco.AuthU.Web.BaseOAuthAttribute..ctor(String realm) in C:\projects\umbraco-authu\src\Our.Umbraco.AuthU\Web\BaseOAuthAttribute.cs:riga 19 in Our.Umbraco.AuthU.Web.WebApi.OAuthAttribute..ctor(String realm) in C:\projects\umbraco-authu\src\Our.Umbraco.AuthU\Web\WebApi\OAuthAttribute.cs:riga 17 in System.Reflection.CustomAttribute._CreateCaObject(RuntimeModule pModule, IRuntimeMethodInfo pCtor, Byte** ppBlob, Byte* pEndBlob, Int32* pcNamedArgs) in System.Reflection.CustomAttribute.CreateCaObject(RuntimeModule module, IRuntimeMethodInfo ctor, IntPtr& blob, IntPtr blobEnd, Int32& namedArgs) in System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeModule decoratedModule, Int32 decoratedMetadataToken, Int32 pcaCount, RuntimeType attributeFilterType, Boolean mustBeInheritable, IList derivedAttributes, Boolean isDecoratedTargetSecurityTransparent) in System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeType type, RuntimeType caType, Boolean inherit) in System.RuntimeType.GetCustomAttributes(Boolean inherit) in System.Web.Http.Controllers.HttpControllerDescriptor.InvokeAttributesOnControllerType(HttpControllerDescriptor controllerDescriptor, Type type) in System.Web.Http.Controllers.HttpControllerDescriptor..ctor(HttpConfiguration configuration, String controllerName, Type controllerType) in System.Web.Http.Dispatcher.DefaultHttpControllerSelector.InitializeControllerInfoCache() in System.Lazy`1.CreateValue() --- Fine traccia dello stack da posizione precedente dove è stata generata l'eccezione --- in System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() in System.Lazy`1.get_Value() in System.Web.Http.Dispatcher.DefaultHttpControllerSelector.GetControllerMapping() in Umbraco.Web.WebApi.NamespaceHttpControllerSelector.SelectController(HttpRequestMessage request) in System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()

biapar commented 7 years ago

@mattbrailsford here is the problem http://issues.umbraco.org/issue/U4-8734

I solved as I described here and found why not login: IDX10223: Lifetime validation failed. The token is expired. ValidTo: '06/15/2017 08:12:01' Current time: '06/16/2017 10:42:24'.

mattbrailsford commented 7 years ago

So you've got it working?

biapar commented 7 years ago

I've always not authorize, but at least your code is executed

biapar commented 7 years ago

Now works!!!!!!!!!!!!!! I changed decoration to [Authorize] from [Umbraco.Web.WebApi.UmbracoAuthorize]

[EnableCors("*", "*", "*")] [Authorize] [HttpGet] public string HelloWorld() { return "Hello " + Members.GetCurrentMember()?.Name; }

mattbrailsford commented 7 years ago

Ahhh, I didn't notice you'd used UmbracoAuthorize (in the initial code example, you seem to be using standard Authorize which is correct). Yes, you want to use Authorize and we aren't really asking umbraco to do anything here, we are using the standard authorize process. Glad you got it working.

NazirAhmad123 commented 6 years ago

Still confused.

biapar commented 6 years ago

@NazirAhmad123 Why Are you confused?

VenkateshIkomet commented 2 years ago

I am tried with both attributes. But it doesn't worked for me

image