Search code examples
oauth-2.0asp.net-coreopenid-connectaspnet-contrib

OpenID Connect server with ASOS, .NET Core pipeline


I have started playing with OpenID Connect server with ASOS by implementing the resource owner password credential grant. however when I test it using postman, I am getting generic 500 internal server error.

Here is my code for your debugging pleasure. I appreciate your feedback.

Thanks

-Biruk

here is my Startup.cs

    public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services.

        services.AddAuthentication(options => {
            options.SignInScheme = "ServerCookie";
        });


        services.AddApplicationInsightsTelemetry(Configuration);

        services.AddMvc();

        services.AddSession(options => {
            options.IdleTimeout = TimeSpan.FromMinutes(30);
        });
    }




    public void Configure(IApplicationBuilder app, IHostingEnvironment env, LoggerFactory loggerFactory)
    {

        app.UseOAuthValidation();


        app.UseOpenIdConnectServer(options => {
            // Create your own authorization provider by subclassing
            // the OpenIdConnectServerProvider base class.
            options.Provider = new AuthorizationProvider();

            // Enable the authorization and token endpoints.
           // options.AuthorizationEndpointPath = "/connect/authorize";
            options.TokenEndpointPath = "/connect/token";

            // During development, you can set AllowInsecureHttp
            // to true to disable the HTTPS requirement.
            options.ApplicationCanDisplayErrors = true;
            options.AllowInsecureHttp = true;

            // Note: uncomment this line to issue JWT tokens.
            // options.AccessTokenHandler = new JwtSecurityTokenHandler();
        });

        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();

        app.UseApplicationInsightsRequestTelemetry();

        app.UseApplicationInsightsExceptionTelemetry();

        app.UseMvc();
    }

and here is my AuthorizationProvider.cs

public sealed class AuthorizationProvider : OpenIdConnectServerProvider
{
    public Task<User> GetUser()
    {

        return Task.Run(()=> new User { UserName = "biruk60", Password = "adminUser123" });
    }
    // Implement OnValidateAuthorizationRequest to support interactive flows (code/implicit/hybrid).
    public override Task ValidateTokenRequest(ValidateTokenRequestContext context)
    {
        // Reject the token request 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 resource owner password credentials and refresh token " +
                             "are accepted by this authorization 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);
    }



    public override async Task HandleTokenRequest(HandleTokenRequestContext context)
    {
        //// Resolve ASP.NET Core Identity's user manager from the DI container.
        //var manager = context.HttpContext.RequestServices.GetRequiredService<UserManager<ApplicationUser>>();

        // Only handle grant_type=password requests and let ASOS
        // process grant_type=refresh_token requests automatically.
        if (context.Request.IsPasswordGrantType())
        {
            // var user = await manager.FindByNameAsync(context.Request.Username);

            var user = await GetUser();//new { userName = "[email protected]", password = "adminUser123" };
            if (user == null)
            {
                context.Reject(
                    error: OpenIdConnectConstants.Errors.InvalidGrant,
                    description: "Invalid credentials.");

                return;
            }

            if (user != null && (user.Password == context.Request.Password))
            {




                var identity = new ClaimsIdentity(context.Options.AuthenticationScheme);

                // Note: the name identifier is always included in both identity and
                // access tokens, even if an explicit destination is not specified.
               // identity.AddClaim(ClaimTypes.NameIdentifier, await manager.GetUserId(user));

                // When adding custom claims, you MUST specify one or more destinations.
                // Read "part 7" for more information about custom claims and scopes.
                identity.AddClaim("username", "biruk60",
                    OpenIdConnectConstants.Destinations.AccessToken,
                    OpenIdConnectConstants.Destinations.IdentityToken);

                // Create a new authentication ticket holding the user identity.
                var ticket = new AuthenticationTicket(
                    new ClaimsPrincipal(identity),
                    new AuthenticationProperties(),
                    context.Options.AuthenticationScheme);

                // Set the list of scopes granted to the client application.
                ticket.SetScopes(
                    /* openid: */ OpenIdConnectConstants.Scopes.OpenId,
                    /* email: */ OpenIdConnectConstants.Scopes.Email,
                    /* profile: */ OpenIdConnectConstants.Scopes.Profile);

                // Set the resource servers the access token should be issued for.
               // ticket.SetResources("resource_server");

                context.Validate(ticket);
            }
        }
    }


}

What am i doing wrong. I can put it in debug mode and step through it without any error it just 500 internal Server Error in fiddler and postman.


Solution

  • Here's the exception you're likely seeing:

    System.InvalidOperationException: A unique identifier cannot be found to generate a 'sub' claim: make sure to add a 'ClaimTypes.NameIdentifier' claim.

    Add a ClaimTypes.NameIdentifier claim and it should work.