1

I have a simple application which uses Angular as front-end and a .NET Core Web API as back-end services. Now I want to secure my WEB API layer. I though I can use OpenID Connect for that purpose. But all the examples or documentation online uses some identity management systems to like (Keycloak, Okta) but i just want to use my user data from a SQL database.

So something like, I hit the WEB API from Angular to get the token generated(using OpenID?) based on the user details sent. The i can just use the token to Authorize users. I want to use OpenID so that i can use some other identity management systems later if i want to.

my startup class in WEB API

services.AddAuthentication(options =>
            {
                options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;

            })
            .AddCookie()
            .AddOpenIdConnect(o =>
            {
                o.ClientId = "sa";
                o.ClientSecret = "sa";
                o.Authority = "https://localhost:44352";
                o.GetClaimsFromUserInfoEndpoint = true;
                o.RequireHttpsMetadata = true;
            });

I added a controller with Authorize attribute and added a test method to see what happens when i hit that from Swagger

I see the following error

IOException: IDX20804: Unable to retrieve document from: 'https://localhost:44352/.well-known/openid-configuration'

I am not sure what the error is.

Also I would like to ask if i doing this correct. Can i use the same API (Authority? as in ( o.Authority = "https://localhost:44352";)) to authenticate/get token from).

2 Answers 2

2

What you'll need for OpenIDConnect is definitely a Server that implements the oidc-spec. The .well-known/... url is part of the oidc discovery spec, which Servers like identityserver and keycloak implement. It gives a standardized List of other endpoints to retrieve tokens, get userinfo, get logouturi etc.

Your API does not have such an endpoint, so that's what the error says.

If you want to implement the whole oidc-spec on your own, go for it, but I wouldn't recommend it, it's kind of complex. As I see it, .net core does only implement an openidconnect-client and you're free to choose the server implementation as long as it implements the spec.

Alternatively, have a look at JwtBearer, which is a more lightweight approach to authentication and more easy to implement yourself (and, best of: you could easily change to oidc later on). Good starting points might be these blogposts.

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

Comments

-2
 services.AddAuthentication(options => 
            {
                options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            }).AddCookie(CookieAuthenticationDefaults.AuthenticationScheme,
                options =>
                {
                    options.LoginPath = new PathString("/");
                    options.AccessDeniedPath = "/Identity/Account/AccessDenied";
                    options.Cookie = new CookieBuilder()
                    {
                        SecurePolicy = CookieSecurePolicy.SameAsRequest,
                        Path = "/"
                    };
                    options.SlidingExpiration = true;
                    options.ExpireTimeSpan = TimeSpan.FromMinutes(sessionTimeout);

                }).AddOpenIdConnect(options =>
                {
                    options.ClientId = Configuration.GetValue<string>("oidc:ClientId");
                    options.ClientSecret = Configuration["oidc:ClientSecret"];
                    options.CallbackPath = new PathString("/auth/callback");
                    options.GetClaimsFromUserInfoEndpoint = true;
                    options.Authority = Configuration["oidc:Authority"];
                    options.SignedOutRedirectUri = "/";
                    options.RequireHttpsMetadata = false;
                    options.SaveTokens = true;
                    options.UseTokenLifetime = true;
                    var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration["oidc:ClientSecret"]));

                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        RequireSignedTokens = true,
                        IssuerSigningKey = signingKey,
                        ValidateAudience = true,
                        ValidateIssuer = true,
                        ValidateIssuerSigningKey = true,
                    };
                    options.ResponseType = OpenIdConnectResponseType.Code;
                    options.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet;

                    options.Scope.Clear();
                    options.Scope.Add("openid");
                    options.Scope.Add("email");
                    options.Scope.Add("profile");

                    options.Events = new OpenIdConnectEvents()
                    {
                        OnTicketReceived = context =>
                        {
                            var identity = context.Principal.Identity as ClaimsIdentity;
                            if (identity != null)
                            {
                                if (!context.Principal.HasClaim(c => c.Type == ClaimTypes.Name) &&
                                        identity.HasClaim(c => c.Type == "name"))
                                    identity.AddClaim(new Claim(ClaimTypes.Name, identity.FindFirst("name").Value));
                                if (context.Properties.Items.ContainsKey(".TokenNames"))
                                {
                                    string[] tokenNames = context.Properties.Items[".TokenNames"].Split(';');

                                    foreach (var tokenName in tokenNames)
                                    {
                                        string tokenValue = context.Properties.Items[$".Token.{tokenName}"];
                                        identity.AddClaim(new Claim(tokenName, tokenValue));
                                    }
                                }
                            }

                            var cp = new ClaimsPrincipal(identity);
                            context.Principal = cp;
                            return Task.CompletedTask;
                        },
                        //OnTokenValidated = context =>
                        //{

                        //    ClaimsIdentity identity = (ClaimsIdentity)context.Principal.Identity;
                        //    Claim Name = identity.FindFirst("preferred_username");
                        //    Claim gender = identity.FindFirst(ClaimTypes.Gender);
                        //    Claim sub = identity.FindFirst(ClaimTypes.NameIdentifier);
                        //    var claimsToKeep = new List<Claim> { Name, gender, sub };

                        //    var newIdentity = new ClaimsIdentity(claimsToKeep, identity.AuthenticationType);

                        //    context.Principal = new ClaimsPrincipal(newIdentity);

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

                        OnAuthenticationFailed = context =>
                        {
                            context.Response.Redirect("/");
                            context.HandleResponse();
                            return Task.CompletedTask;
                        },
                        OnRedirectToIdentityProvider = context =>
                        {
                            context.ProtocolMessage.SetParameter("pfidpadapterid", Configuration["oidc:pfidpadapterid"]);
                            return Task.FromResult(0);
                        }
                    };
                });

1 Comment

Code-only answers are generally frowned upon on this site. Could you please edit your answer to include some comments or explanation of your code? Explanations should answer questions like: What does it do? How does it do it? Where does it go? How does it solve OP's problem?

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.