6

My ASP.NET 5 (MVC 6 + beta7) web application (MVC + WebAPI) is required to get back an access_token from WebAPI login calls.

So far, from googling, I have created the following code for startup.cs:

app.UseOAuthBearerAuthentication(options => {
    options.AutomaticAuthentication = true;
    options.Audience = "http://localhost:62100/";
    options.Authority = "http://localhost:62100/";
});

My client side is:

var login = function ()
{
    var url = "http://localhost:62100/";
    var data = $("#userData").serialize();
    data = data + "&grant_type=password";
    $.post(url, data)
        .success(saveAccessToken)
        .always(showResponse);
    return false;
};

Is it required to use UseOpenIdConnectServer? If so, how do I use SigningCredentials so that I get a token (e.g. MVC5 ApplicationOAuthProvider)?

Please note that my site is simple demo HTTP site and I do not need any SSL.

1 Answer 1

6

Is it required to use UseOpenIdConnectServer?

Using AspNet.Security.OpenIdConnect.Server is not "required". You're - of course - free to opt for another server (like IdentityServer) or for a custom solution. Being the main developer behind aspnet-contrib, I'm not really objective, so I'll necessarily suggest going with app.UseOpenIdConnectServer().

If so, how do I use SigningCredentials so that I get a token (e.g. MVC5 ApplicationOAuthProvider)?

When implementing the password and using the default token type, registering a signing key/certificate is not mandatory.

Here's how you can get started:

ASP.NET Core 1.x:

Startup.cs

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication();
    }

    public void Configure(IApplicationBuilder app)
    {
        // Add a new middleware validating the encrypted
        // access tokens issued by the OIDC server.
        app.UseOAuthValidation();

        // Add a new middleware issuing tokens.
        app.UseOpenIdConnectServer(options =>
        {
            options.TokenEndpointPath = "/connect/token";

            // Override OnValidateTokenRequest to skip client authentication.
            options.Provider.OnValidateTokenRequest = context =>
            {
                // Reject the 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.FromResult(0);
                }

                // Since there's only one application and since it's a public client
                // (i.e a client that cannot keep its credentials private),
                // call Skip() to inform the server the request should be
                // accepted without enforcing client authentication.
                context.Skip();

                return Task.FromResult(0);
            };

            // Override OnHandleTokenRequest to support
            // grant_type=password token requests.
            options.Provider.OnHandleTokenRequest = context =>
            {
                // Only handle grant_type=password token requests and let the
                // OpenID Connect server middleware handle the other grant types.
                if (context.Request.IsPasswordGrantType())
                {
                    // Do your credentials validation here.
                    // Note: you can call Reject() with a message
                    // to indicate that authentication failed.

                    var identity = new ClaimsIdentity(context.Options.AuthenticationScheme);
                    identity.AddClaim(OpenIdConnectConstants.Claims.Subject, "[unique id]");

                    // By default, claims are not serialized
                    // in the access and identity tokens.
                    // Use the overload taking a "destinations"
                    // parameter to make sure your claims
                    // are correctly inserted in the appropriate tokens.
                    identity.AddClaim("urn:customclaim", "value",
                        OpenIdConnectConstants.Destinations.AccessToken,
                        OpenIdConnectConstants.Destinations.IdentityToken);

                    var ticket = new AuthenticationTicket(
                        new ClaimsPrincipal(identity),
                        new AuthenticationProperties(),
                        context.Options.AuthenticationScheme);

                    // Call SetScopes with the list of scopes you want to grant
                    // (specify offline_access to issue a refresh token).
                    ticket.SetScopes("profile", "offline_access");

                    context.Validate(ticket);
                }

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

.csproj

<ItemGroup>
  <PackageReference Include="AspNet.Security.OpenIdConnect.Server" Version="1.0.2" />
</ItemGroup>

ASP.NET Core 2.x:

Startup.cs

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication()
            // Add a new middleware validating the encrypted
            // access tokens issued by the OIDC server.
            .AddOAuthValidation()

            // Add a new middleware issuing tokens.
            .AddOpenIdConnectServer(options =>
            {
                options.TokenEndpointPath = "/connect/token";

                // Override OnValidateTokenRequest to skip client authentication.
                options.Provider.OnValidateTokenRequest = context =>
                {
                    // Reject the 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;
                    }

                    // Since there's only one application and since it's a public client
                    // (i.e a client that cannot keep its credentials private),
                    // call Skip() to inform the server the request should be
                    // accepted without enforcing client authentication.
                    context.Skip();

                    return Task.CompletedTask;
                };

                // Override OnHandleTokenRequest to support
                // grant_type=password token requests.
                options.Provider.OnHandleTokenRequest = context =>
                {
                    // Only handle grant_type=password token requests and let the
                    // OpenID Connect server middleware handle the other grant types.
                    if (context.Request.IsPasswordGrantType())
                    {
                        // Do your credentials validation here.
                        // Note: you can call Reject() with a message
                        // to indicate that authentication failed.

                        var identity = new ClaimsIdentity(context.Scheme.Name);
                        identity.AddClaim(OpenIdConnectConstants.Claims.Subject, "[unique id]");

                        // By default, claims are not serialized
                        // in the access and identity tokens.
                        // Use the overload taking a "destinations"
                        // parameter to make sure your claims
                        // are correctly inserted in the appropriate tokens.
                        identity.AddClaim("urn:customclaim", "value",
                            OpenIdConnectConstants.Destinations.AccessToken,
                            OpenIdConnectConstants.Destinations.IdentityToken);

                        var ticket = new AuthenticationTicket(
                            new ClaimsPrincipal(identity),
                            new AuthenticationProperties(),
                            context.Scheme.Name);

                        // Call SetScopes with the list of scopes you want to grant
                        // (specify offline_access to issue a refresh token).
                        ticket.SetScopes("profile", "offline_access");

                        context.Validate(ticket);
                    }

                    return Task.CompletedTask;
                };
            });
    }
}

.csproj

<ItemGroup>
  <PackageReference Include="AspNet.Security.OpenIdConnect.Server" Version="2.0.0-*" />
</ItemGroup>

You can also read this blog post, that explains how to implement the resource owner password grant: http://kevinchalet.com/2016/07/13/creating-your-own-openid-connect-server-with-asos-implementing-the-resource-owner-password-credentials-grant/

Sign up to request clarification or add additional context in comments.

29 Comments

Thanks very much for your answer. So I could not work out this in Beta 7, correct? When I move to Beta 8, I get so many dependency build errors, so would be more happy if I can figure out in beta 7...
You can find a version compatible with ASP.NET beta7 on NuGet.org but it won't offer you the automatic key generation feature. We'll move to beta8 soon and update the corresponding packages on NuGet. In the meantime, you can try our nightly builds.
Thanks very much for this. When I convert to beta8, i get lot of build error messages from cs files in migration folder. Do you have any suggestion?
I think you must remove them (and optionally recreate them if you need to). FYI, I just uploaded the beta3 package of AspNet.Security.OpenIdConnect.Server (for ASP.NET 5 beta8) on NuGet.org: nuget.org/packages/AspNet.Security.OpenIdConnect.Server/…. I've updated my answer to reflect that.
Thanks very much for your reply. I have recreated the project in Beta8 and now those errors are gone. However I can not Install the Package AspNet.Security.OpenIdConnect.Server beta 3 due to following error message. Can you please specify the location of beta3 of this package? Install-Package : Unable to find package
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.