Openiddict-core: OpenIddict Core with SignalR

Created on 17 Sep 2018  路  2Comments  路  Source: openiddict/openiddict-core

I've been looking all over, and cannot figure out how to get the access token from a query string to be used with Signalr. Could somebody by chance help provide an example?

I've tried doing both this:

.AddValidation(options =>
{
    options.AddEventHandler<OpenIddictValidationEvents.RetrieveToken>(
        notification =>
        {
            notification.Context.Token = notification.Context.Request.Query["access_token"];

            return Task.CompletedTask;
        });
});

and this:

services.AddAuthentication()
    .AddOAuthValidation(options =>
    {
        options.Events.OnRetrieveToken = context =>
        {
            context.Token = context.Request.Query["access_token"];

            return Task.CompletedTask;
        };
    });

in my Startup.cs file (as found in another issue here)

Is there something I am missing? I'm using .net core 2.1. Thank you so much in advance for any help! I've been stuck on this for quite a while.

Most helpful comment

Okay I feel a little stupid, and finally figured this out (ironically pretty quickly after posting the issue). The key is that if you add to the OAuthValidation options - in services.AddOauthValidation(options => ...), then you must completely comment out the .AddValidation(); after .AddServer in services.AddOpenIddict();

For example here's a sample of working code, that does not use JWT tokens:

services.AddOpenIddict()
    .AddCore(options =>
    {
        // Configure OpenIddict to use the default entities.
        //options.UseDefaultModels();

        // Register the Entity Framework stores.
        //options.AddEntityFrameworkCoreStores<DefaultDbContext>();
        options.UseEntityFrameworkCore()
        .UseDbContext<DefaultDbContext>();
    })

            .AddServer(options =>
            {
                // Register the ASP.NET Core MVC binder used by OpenIddict.
                // Note: if you don't call this method, you won't be able to
                // bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
                //options.AddMvcBinders();
                options.UseMvc();

                // Enable the authorization, logout, token and userinfo endpoints.
                options.EnableAuthorizationEndpoint("/api/connect/authorize")
                        .EnableLogoutEndpoint("/api/connect/logout")
                        .EnableTokenEndpoint("/api/connect/token")
                        .EnableUserinfoEndpoint("/api/userinfo");

                // Allow client applications to use the code flow.
                // Allow client applications to use the grant_type=password flow.
                options.AllowAuthorizationCodeFlow()
                        .AllowPasswordFlow()
                        .AllowRefreshTokenFlow()
                        .AllowCustomFlow("urn:ietf:params:oauth:grant-type:facebook_access_token");

                // Mark the "profile" scope as a supported scope in the discovery document.
                options.RegisterScopes(OpenIdConnectConstants.Scopes.Email,
                    OpenIdConnectConstants.Scopes.Profile,
                    OpenIddictConstants.Scopes.Roles);

                options.SetAccessTokenLifetime(TimeSpan.FromDays(1));

                // When request caching is enabled, authorization and logout requests
                // are stored in the distributed cache by OpenIddict and the user agent
                // is redirected to the same page with a single parameter (request_id).
                // This allows flowing large OpenID Connect requests even when using
                // an external authentication provider like Google, Facebook or Twitter.
                options.EnableRequestCaching();

                // During development, you can disable the HTTPS requirement.
                options.DisableHttpsRequirement();
            });
            //.AddValidation();

services.AddAuthentication()
        .AddOAuthValidation(options =>
        {
            options.Events.OnRetrieveToken = context =>
            {
                context.Token = context.Request.Query["access_token"];

                return Task.CompletedTask;
            };
        });

Now the access_token in the query string will work to authenticate your users with Signalr - by placing the [Authorize(AuthenticationSchemes = OAuthValidationDefaults.AuthenticationScheme)] above the Hub class, just as you would in the Controller.

All 2 comments

My code on the client side looks like this:

this.connection = new this.$signalR.HubConnectionBuilder()
    .withUrl("/authTest?access_token=" + token)
    .configureLogging(this.$signalR.LogLevel.Error)
    .build();

which gives this error:
signalr.min.js:16 POST https://localhost:44318/authTest/negotiate?access_token=XXXXXX 401 ()

Okay I feel a little stupid, and finally figured this out (ironically pretty quickly after posting the issue). The key is that if you add to the OAuthValidation options - in services.AddOauthValidation(options => ...), then you must completely comment out the .AddValidation(); after .AddServer in services.AddOpenIddict();

For example here's a sample of working code, that does not use JWT tokens:

services.AddOpenIddict()
    .AddCore(options =>
    {
        // Configure OpenIddict to use the default entities.
        //options.UseDefaultModels();

        // Register the Entity Framework stores.
        //options.AddEntityFrameworkCoreStores<DefaultDbContext>();
        options.UseEntityFrameworkCore()
        .UseDbContext<DefaultDbContext>();
    })

            .AddServer(options =>
            {
                // Register the ASP.NET Core MVC binder used by OpenIddict.
                // Note: if you don't call this method, you won't be able to
                // bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
                //options.AddMvcBinders();
                options.UseMvc();

                // Enable the authorization, logout, token and userinfo endpoints.
                options.EnableAuthorizationEndpoint("/api/connect/authorize")
                        .EnableLogoutEndpoint("/api/connect/logout")
                        .EnableTokenEndpoint("/api/connect/token")
                        .EnableUserinfoEndpoint("/api/userinfo");

                // Allow client applications to use the code flow.
                // Allow client applications to use the grant_type=password flow.
                options.AllowAuthorizationCodeFlow()
                        .AllowPasswordFlow()
                        .AllowRefreshTokenFlow()
                        .AllowCustomFlow("urn:ietf:params:oauth:grant-type:facebook_access_token");

                // Mark the "profile" scope as a supported scope in the discovery document.
                options.RegisterScopes(OpenIdConnectConstants.Scopes.Email,
                    OpenIdConnectConstants.Scopes.Profile,
                    OpenIddictConstants.Scopes.Roles);

                options.SetAccessTokenLifetime(TimeSpan.FromDays(1));

                // When request caching is enabled, authorization and logout requests
                // are stored in the distributed cache by OpenIddict and the user agent
                // is redirected to the same page with a single parameter (request_id).
                // This allows flowing large OpenID Connect requests even when using
                // an external authentication provider like Google, Facebook or Twitter.
                options.EnableRequestCaching();

                // During development, you can disable the HTTPS requirement.
                options.DisableHttpsRequirement();
            });
            //.AddValidation();

services.AddAuthentication()
        .AddOAuthValidation(options =>
        {
            options.Events.OnRetrieveToken = context =>
            {
                context.Token = context.Request.Query["access_token"];

                return Task.CompletedTask;
            };
        });

Now the access_token in the query string will work to authenticate your users with Signalr - by placing the [Authorize(AuthenticationSchemes = OAuthValidationDefaults.AuthenticationScheme)] above the Hub class, just as you would in the Controller.

Was this page helpful?
0 / 5 - 0 ratings