IdentityServer / IdentityServer3.AccessTokenValidation

OWIN Middleware to validate access tokens from IdentityServer3
Apache License 2.0
91 stars 149 forks source link

Using UseIdentityServerBearerTokenAuthentication in IdentityServer3 #47

Closed vindberg closed 9 years ago

vindberg commented 9 years ago

Im trying to add an users API in my identityserver (IdentityServer3 2.x) solution.

Im getting this error when adding the UseIdentityServerBearerTokenAuthentication to startup. Is there a conflict with the other mappings of "core" or? Thanks in advance.

Some code from Startup:

            // API Config
            app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
            {
                Authority = Settings.Default.Authority,
                RequiredScopes = new[] { "idmgr" }
            });

            // web api configuration
            var config = new HttpConfiguration();
            config.Formatters.Remove(config.Formatters.XmlFormatter);
            config.MapHttpAttributeRoutes();

            app.UseWebApi(config);

The error:

[NullReferenceException: Object reference not set to an instance of an object.]
   IdentityServer3.AccessTokenValidation.DiscoveryDocumentIssuerSecurityTokenProvider..ctor(String discoveryEndpoint, IdentityServerBearerTokenAuthenticationOptions options, ILoggerFactory loggerFactory) in c:\Dropbox\identity\IdentityServer3\AccessTokenValidation\source\AccessTokenValidation\Plumbing\DiscoveryDocumentIssuerSecurityTokenProvider.cs:43
   Owin.IdentityServerBearerTokenValidationAppBuilderExtensions.ConfigureLocalValidation(IdentityServerBearerTokenAuthenticationOptions options, ILoggerFactory loggerFactory) in c:\Dropbox\identity\IdentityServer3\AccessTokenValidation\source\AccessTokenValidation\IdentityServerBearerTokenValidationAppBuilderExtensions.cs:105
   Owin.IdentityServerBearerTokenValidationAppBuilderExtensions.UseIdentityServerBearerTokenAuthentication(IAppBuilder app, IdentityServerBearerTokenAuthenticationOptions options) in c:\Dropbox\identity\IdentityServer3\AccessTokenValidation\source\AccessTokenValidation\IdentityServerBearerTokenValidationAppBuilderExtensions.cs:50
   Identity.Jeded.Web.Startup.Configuration(IAppBuilder app) in ..\Source\Workspaces\Jeded\Identity.Jeded\Identity.Jeded.Web\Startup.cs:120

[TargetInvocationException: Exception has been thrown by the target of an invocation.]
   System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor) +0
   System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments) +128
   System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) +146
   Owin.Loader.<>c__DisplayClass12.<MakeDelegate>b__b(IAppBuilder builder) +93
   Owin.Loader.<>c__DisplayClass1.<LoadImplementation>b__0(IAppBuilder builder) +209
   Microsoft.Owin.Host.SystemWeb.OwinAppContext.Initialize(Action`1 startup) +843
   Microsoft.Owin.Host.SystemWeb.OwinBuilder.Build(Action`1 startup) +51
   Microsoft.Owin.Host.SystemWeb.OwinHttpModule.InitializeBlueprint() +101
   System.Threading.LazyInitializer.EnsureInitializedCore(T& target, Boolean& initialized, Object& syncLock, Func`1 valueFactory) +141
   Microsoft.Owin.Host.SystemWeb.OwinHttpModule.Init(HttpApplication context) +172
   System.Web.HttpApplication.RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers) +619
   System.Web.HttpApplication.InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context) +175
   System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(IntPtr appContext, HttpContext context) +441
   System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr appContext) +360

[HttpException (0x80004005): Exception has been thrown by the target of an invocation.]
   System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +579
   System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +112
   System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +726
brockallen commented 9 years ago

Try: RequiredScopes = new string[] { "idmgr" }

vindberg commented 9 years ago

Unfortunately its not the issue. The configuration is from the MVC Authorization example ("string" not there).

The exception happens directly at: app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions

brockallen commented 9 years ago

Are you're sure Authority is not null?

vindberg commented 9 years ago

Yes, its same result if I hard-code it as string. Here are my complete startup config if that can help:

        public void Configuration(IAppBuilder app)
        {
            Log.Logger = new LoggerConfiguration()
                                .WriteTo.ExceptionLess(b => b.AddTags("IdentityServer").AddRequestInfo())
                                .CreateLogger();

            var defaultExceptionlessClient = ExceptionlessClient.Default;
            defaultExceptionlessClient.Configuration.UseInMemoryStorage();
            defaultExceptionlessClient.Register();

            new Exception("System Startup").ToExceptionless().Submit();

            AntiForgeryConfig.UniqueClaimTypeIdentifier = Constants.ClaimTypes.Subject;
            JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();

            app.Map("/core", core =>
            {
                var idSvrFactory = Factory.ConfigureClientsAndScopes(Settings.Default.ConnectionStringName);
                idSvrFactory.ConfigureUserService(Settings.Default.ConnectionStringName);

                var viewOptions = new DefaultViewServiceOptions();
                viewOptions.Stylesheets.Add("/Content/New/Site.css");
                viewOptions.CacheViews = false;
                idSvrFactory.ConfigureDefaultViewService(viewOptions);

                var options = new IdentityServerOptions
                {
                    SiteName = "Identity Server",
                    SigningCertificate = Certificate.Get(),
                    Factory = idSvrFactory,
                    EnableWelcomePage = false,

                    AuthenticationOptions = new AuthenticationOptions
                    {
                        IdentityProviders = ConfigureIdentityProviders,
                        EnablePostSignOutAutoRedirect = true,
                        LoginPageLinks = new LoginPageLink[] {
                        new LoginPageLink{
                            Text = "Register",
                            Href = "~/registration"
                            },
                                                new LoginPageLink{
                            Text = "Forgot Password?",
                            Href = "~/forgotpassword"
                            }

                        }
                    }
                };

                core.UseIdentityServer(options);
            });
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = CookieAuthenticationDefaults.AuthenticationType
            });

            app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
            {
                Authority = Settings.Default.Authority,
                ClientId = "idmgr",
                RedirectUri = Settings.Default.IdMgrRedirectUri,
                ResponseType = "id_token",
                Scope = "openid idmgr",
                SignInAsAuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
            });

            app.Map("/admin", adminApp =>
            {
                var factory = new IdentityManagerServiceFactory();
                factory.ConfigureSimpleIdentityManagerService(Settings.Default.ConnectionStringName);

                adminApp.UseIdentityManager(new IdentityManagerOptions()
                {
                    Factory = factory,
                    SecurityConfiguration = new HostSecurityConfiguration()
                    {
                        HostAuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
                        NameClaimType = Constants.ClaimTypes.Subject,
                        RoleClaimType = Constants.ClaimTypes.Role,
                        AdminRoleName = "SystemAdministrator",
                    },
                });
            });

            // API Config
            app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
            {
                Authority = Settings.Default.Authority,
                RequiredScopes = new string[] { "idmgr" }
            });

            // web api configuration
            var config = new HttpConfiguration();
            config.Formatters.Remove(config.Formatters.XmlFormatter);
            config.MapHttpAttributeRoutes();

            app.UseWebApi(config);
        }
leastprivilege commented 9 years ago

If both idsrv and the access token val MW is in the same app - there might be a race condition. Since the discovery endpoint might not be up yet when.

In 2.2 we allow configuring the val MW statically (released today)

vindberg commented 9 years ago

That sounds like a solution. Where should I look?

Thanks mate.

leastprivilege commented 9 years ago

in the docs - of course ;)

vindberg commented 9 years ago

Found it: https://identityserver.github.io/Documentation/docsv2/consuming/options.html

Its working when IssuerName and SigningCertificate is added to the API config. But somehow the API config needs to be above the Identity manager mapping. If its below app.Map("/admin", adminApp => ...) then it still fails.

leastprivilege commented 9 years ago

yeah order matters.

dcinzona commented 8 years ago

@leastprivilege I know this is closed, but we are seeing a similar issue (same error when enabling the feature). When you mention that order matters, can you expand on that? Why does it fail if executed after the mapping? Also, we are not using Identity Manager, just IdSrv 2.3 with EF.
In our case, much as above, we have the IdSrv and MVC client app running in the same application. There are 3 OWIN middlewares that are loaded separately in this order:

  1. ID Server (Mapped to /identity)
  2. MVC with API (Orchard CMS mapped to root)
  3. app.UseIdentityServerBearerTokenAuthentication

It doesn't matter if we initialize UseIdentityServerBearerTokenAuthentication in the same MVC startup, we still get the exact same error.

System.NullReferenceException: Object reference not set to an instance of an object.
   at IdentityServer3.AccessTokenValidation.ValidationEndpointTokenProvider..ctor(IdentityServerBearerTokenAuthenticationOptions options, ILoggerFactory loggerFactory)
   at Owin.IdentityServerBearerTokenValidationAppBuilderExtensions.ConfigureEndpointValidation(IdentityServerBearerTokenAuthenticationOptions options, ILoggerFactory loggerFactory)
   at Owin.IdentityServerBearerTokenValidationAppBuilderExtensions.UseIdentityServerBearerTokenAuthentication(IAppBuilder app, IdentityServerBearerTokenAuthenticationOptions options)
   at NFLPA.Web.Module.IdentityClient.StartupOAuthAPIFeature.<>c.<GetOwinMiddlewares>b__0_0(IAppBuilder app)
dcinzona commented 8 years ago

It looks like the issue may be with the logger actually. app.GetLoggerFactory() returns null and because of that, my guess is that it fails here: https://github.com/IdentityServer/IdentityServer3.AccessTokenValidation/blob/dc9d280f75feb093dc2516f44583f6ca6d0f9c2b/source/AccessTokenValidation/Plumbing/ValidationEndpointTokenProvider.cs#L39

mcianc commented 8 years ago

I have the same problem... when updated from 2.0 to 2.5 IDS3 server. Me too is a concurrency problem.. app.UseIdentityServerBearerTokenAuthentication(...); try to request discovery document in time t0 where IDS server is not ready.

A unique project with IndentityServer and API resources in a single assembly, there is some workareound? SOrry but I dind't understood if was solved.

brockallen commented 8 years ago

Did you set DelayLoadMetadata? https://github.com/IdentityServer/IdentityServer3.AccessTokenValidation/blob/master/source/AccessTokenValidation/IdentityServerBearerTokenAuthenticationOptions.cs#L190

leastprivilege commented 8 years ago

It's even documented (now) ;)) https://identityserver.github.io/Documentation/docsv2/consuming/options.html

florindpreda commented 8 years ago

I can confirm this is still happening:

[NullReferenceException: Object reference not set to an instance of an object.]
   IdentityServer3.AccessTokenValidation.IdentityServerBearerTokenValidationMiddleware..ctor(Func`2 next, IAppBuilder app, IdentityServerOAuthBearerAuthenticationOptions options, ILoggerFactory loggerFactory) in c:\local\identity\server3\AccessTokenValidation\source\AccessTokenValidation\IdentityServerBearerTokenValidationMiddleware.cs:52
   lambda_method(Closure , Func`2 , IAppBuilder , IdentityServerOAuthBearerAuthenticationOptions , ILoggerFactory )

IdentityServer, Manager, and Admin in the same project. Tried DelayLoadMetadata with no success.

It doesn't throw an exception when I move it before the Manager auth setup but then I can no longer access the Manager API.

I'm basically trying to expose a User API which will only be used from an application previously authenticated via IdentityServer3.

Any ideas?

CSharped commented 8 years ago

@florindpreda , did you manage to find a solution for this? I am exactly at the same point as you are

CSharped commented 8 years ago

It works for me now, the order matters really!!!! I put the api config before Identityserver settings

florindpreda commented 8 years ago

@FullyCSharped Glad to hear that it's working! It was a nice to have for me, so I ended up using basic auth + encryption on my scenario.

Can you please share your Startup.cs file? Might help others in the future.

thienly commented 7 years ago

@FullyCSharped Could you please share the startup file. I also have the issue.

UlyssesAlves commented 7 years ago

@leastprivilege About the docs for static JWTs configuration, what am I expected to put in the IssuerName field? I'm trying to understand what I need to do to use a common name both in this IssuerName field and in my IdentityServer3 server, so that, when the authentication/authorization proccesses are executed, everything will work as expected. That is, my IdentityServer3 server will be properly found by my ASP.NET WebApi server.

travismartinjones commented 7 years ago

I can confirm that the reported solution from @dcinzona resolved this issue for me. Simply place app.SetLoggerFactory(new DiagnosticsLoggerFactory()); on the line before app.UseIdentityServerBearerTokenAuthentication(....

jitender82 commented 6 years ago

We are using app.UseIdentityServerBearerTokenAuthentication( New IdentityServerBearerTokenAuthenticationOptions() With { .Authority = "https://first.com:443", .ValidationMode = ValidationMode.ValidationEndpoint, .ValidationResultCacheDuration = New TimeSpan(0, 0, 200), .EnableValidationResultCache = True, }) in our web API and on https://first.com:443 our identity server is running which validate the token. How could we know that identity server is down ?