Token based implementation in webapi to secure endpoints

前端 未结 2 997
难免孤独
难免孤独 2020-12-12 00:44

I am having a web application with web service and client will register their application using my web application.

Now client will have application of type

相关标签:
2条回答
  • 2020-12-12 01:26

    No, you don't need to store the access_token on the database. You can decrypt the JWT and read the information as you are the one who encrypts it with a secret key. (By default it's the machine key.)

    Identity has a off the self support for Oauth. You have to just configure it properly. You can set-up the configuration for OAuthAuthorizationServerOptions in the Startup.Auth.cs. Sample code as follows. I have tried to answer most of your question in comments in the code.

    public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
    
        public static string PublicClientId { get; private set; }
    
        public void ConfigureOAuth(IAppBuilder app)
        {
            // Configure the application for OAuth based flow
            PublicClientId = "theDragonIsAlive";
            OAuthOptions = new OAuthAuthorizationServerOptions
            {
                TokenEndpointPath = new PathString("/Token"),
                Provider = new YourOwnApplicationOAuthProvider(PublicClientId),
                //AuthorizeEndpointPath = new PathString("/Access/Account"),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(7)
                //AllowInsecureHttp = true
            };
    
            // Enable the application to use bearer tokens to authenticate users
            app.UseOAuthBearerTokens(OAuthOptions);
    
        }
    
    public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
    {
        private readonly string _publicClientId;
    
        public ApplicationOAuthProvider(string publicClientId)
        {
            if (publicClientId == null)
            {
                throw new ArgumentNullException("publicClientId");
            }
    
            _publicClientId = publicClientId;
        }
    
        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
            // This where you are validating the username and password credentials.
            ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
    
            if (user == null)
            {
                context.SetError("Dragon Fire:", "The user name or password is incorrect. You shall be burnt.");
                return;
            }
    
            ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
               OAuthDefaults.AuthenticationType);
    
            AuthenticationProperties properties = CreateProperties(user.UserName);
            AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
            context.Validated(ticket);
            context.Request.Context.Authentication.SignIn(oAuthIdentity);
        }
    
        public override Task TokenEndpoint(OAuthTokenEndpointContext context)
        {
            foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
            {
                context.AdditionalResponseParameters.Add(property.Key, property.Value);
            }
    
            return Task.FromResult<object>(null);
        }
    
        // This method is where you will create the client access token.
        // First you get the client, you can place values from the client record into the tokens claim collection.
        // You then create a new ClaimsIdentity.
        // You add some claims, in the example client name is added.
        // Create an AuthenticationTicket using your claims identity.
        // Validate the ticket (you do need to do this or the client will be considered unauthenticated)
        //public override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
        //{
        //    var client = clientService.GetClient(context.ClientId);
        //    var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
        //    oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, client.ClientName));
        //    var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
        //    context.Validated(ticket);
        //    return base.GrantClientCredentials(context);
        //}
    
        // This method has to be implmented when you are maintaining a list of clients which you will allow.
        // This method is for validating the input, you can used this method to verify the client id and secret are valid.
        public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            //string clientId;
            //string clientSecret;
            //context.TryGetFormCredentials(out clientId, out clientSecret);
    
            //if (clientId == "1234" && clientSecret == "12345")
            //{
            //    context.Validated(clientId);
            //}
    
            //return base.ValidateClientAuthentication(context);
    
            // Resource owner password credentials does not provide a client ID.
            if (context.ClientId == null)
            {
                context.Validated();
            }
    
            return Task.FromResult<object>(null);
        }
    
        public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
        {
            if (context.ClientId == _publicClientId)
            {
                Uri expectedRootUri = new Uri(context.Request.Uri, "/");
    
                if (expectedRootUri.AbsoluteUri == context.RedirectUri)
                {
                    context.Validated();
                }
            }
    
            return Task.FromResult<object>(null);
        }
    
        public static AuthenticationProperties CreateProperties(string userName)
        {
            IDictionary<string, string> data = new Dictionary<string, string>
            {
                { "userName", userName }
            };
            return new AuthenticationProperties(data);
        }
    }
    

    The sample code above does not have separate client classifications. It will treat all users as a single type of client. But I have given some example code in the comments which will guide you to get started in the right direction.

    Disclaimer: I am not an expert on this(yet) and my setup is different. I had an existing MVC application with Owin and I had to build a webapi on top of it. This was my prototype code and it did the job. You will have to improve in it for your production code. Have fun and good luck.

    0 讨论(0)
  • 2020-12-12 01:37

    From “7.1. Access Token Representation” in “Full-Scratch Implementor of OAuth and OpenID Connect Talks About Findings” :

    How should an access token be represented? There are two major ways.

    1. As a meaningless random string. Information associated with an access token is stored in a database table behind an authorization server.

    2. As a self-contained string which is a result of encoding access token information by base64url or something similar.

    Pros and cons of these two ways are described in the blog.

    If access tokens are random strings, pieces of information associated with the access tokens (user ID, client ID, scopes, lifetime, etc.) are stored in a database which is managed by the authorization server which have issued the access tokens.

    Whenever a resource server which exposes APIs accepts an API call from a client application, the resource server has to get the information about the access token in some way or other.

    If the resource server can access the database managed by the authorization server (in other words, if the resource server and the authorization server shares the database), the resource server can get the information about the access token from the database directly.

    Otherwise, the resource server has to make an API call to the authorization server to get the information. In this case, it can be expected that the authorization server exposes an API which complies with RFC 7662 (OAuth 2.0 Token Introspection). Note that some implementations may provide a more developer-friendly API than RFC 7662 (e.g. “4. Introspection Access Token”).

    Anyway, your resource server doesn't necessarily have to make a DB call (or an introspection API call to the authorization server) every time if the server caches information about access tokens in a memory cache or somewhere else appropriate.

    BTW, what you need when you want to protect APIs is access tokens. Therefore, your system doesn't have to support OpenID Connect which is a specification as to how to request and issue ID tokens. You may be confused because a server which supports OpenID Connect can issue access tokens, too, in addition to ID tokens. See “Diagrams of All The OpenID Connect Flows” to understand what a server which supports OpenID Connect issues.

    Finally, identity management, user authentication, and OAuth 2.0 & OpenID Connect don't necessarily have to be implemented in a monolithic way. See “New Architecture of OAuth 2.0 and OpenID Connect Implementation” for details.

    0 讨论(0)
提交回复
热议问题