OAuth2.0 Leaing Note. Authorization Code Grant
Leaing Note About Web Authentication and Authorize
1.we use Owin to implement the Authentication and Authorize.
we create a new Startup.cs file to replace the global.asax file. here is a general content of the startup.cs file.
using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Http;using Microsoft.Owin;using Owin;using Microsoft.Owin.Security.OAuth;using angularjsAuthentication.api.Providers;[assembly:OwinStartup(typeof(angularjsAuthentication.api.Startup))]namespace angularjsAuthentication.api{public class Startup{public void Configuration(IAppBuilder app){HttpConfiguration config = new HttpConfiguration();ConfigureOAuth(app);WebApiConfig.Register(config);app.UseWebApi(config);}public void ConfigureOAuth(IAppBuilder app){OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions(){AllowInsecureHttp = true,TokenEndpointPath = new PathString("/token"),AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),Provider = new SimpleAuthorizationServerProvider(),RefreshTokenProvider = new SimpleRefreshTokenProvider()};app.UseOAuthAuthorizationServer(OAuthServerOptions);app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());}}}
- Some key class : OAuthAuthorizationServerOptions
OAuthAuthorizationServerProvider
we have an important interface IOAuthAuthorizationServerProvider, the OAuthAuthorizationServerOptions provide a default implementation of this interface.
if we have any custom requirement, we can inherite it and override some methods.
2.1 For the first method OAuthAuthorizationServerProvider.ValidateClientAuthentication(), the key point, if validate pass, call context.Validate(), otherwise, call context.setErrors().
2.2 For this class, take care of these methods OAuthAuthorizationServerProvider.GrantResourceOwnerCredentials
this method is reponsible for grantting access token to the request with grant_type as password, if success, call context.validate(token). generally, if a request arrives at token Endpoint with grant_type password, this method will be called.
these sub class AuthenticationTicket, ClaimsIdentity. AuthenticationProperties
2.3 OAuthAuthorizationServerProvider.GrantRefreshToken, called when a request to tokenendpoint with grant_type refresh_token. we can see the http api.
3. OAuth2
3.1 we have a lot of high quality articles descriping this protocol, here is just a link: link1, we can get a lot from cnblogs.
here we just make things simple, OAuth2 support four types of Authorization granttypes: Authorization Code Grant, Implicit Grant, Resource Owener Password Credentials Grant, Client Credential Grant. For each Authorization granttype, we make a note of each method called during a end2end test.
3.1 Resource Owener Password Credentials Grant
first, we request the access token, this method will be called OAuthAuthorizationServerProvider.ValidateClientAuthentication
, this function is called to validate if the client is a registered client. if passed, OAuthAuthorizationServerProvider.GrantResourceOwnerCredentials
will be called. see the msdn .
secondly, if we provide the RefreshTokenProvider which implete the interface IAuthenticationTokenProvider, if user request an access token, the workflow will show like this: OAuthAuthorizationServerProvider.ValidateClientAuthentication -> OAuthAuthorizationServerProvider.GrantResourceOwnerCredentials -> IAuthenticationTokenProvider.CreateAsync -> OAuthAuthorizationServerProvider.TokenEndpoint; if user try to refresh the access token, the workflow will like this: OAuthAuthorizationServerProvider.ValidateClientAuthentication ->
OAuthAuthorizationServerProvider.GrantRefreshToken -> IAuthenticationTokenProvider.ReceiveAsync -> OAuthAuthorizationServerProvider.TokenEndpoint
3.2 Authorization Code Grant
For the detail workflow we have a great doc. Here is a general sample code.
Startup.cs
using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Http;using Microsoft.Owin;using Owin;using Microsoft.Owin.Security.OAuth;using angularjsAuthentication.api.Providers;using System.Data.Entity;using angularjsAuthentication.api.DataRepository;using Microsoft.Owin.Security.Infrastructure;using System.Collections.Concurrent;using log4net;using System.Web.Routing;[assembly:OwinStartup(typeof(angularjsAuthentication.api.Startup))]namespace angularjsAuthentication.api{public class Startup{private static ILog Log = LogManager.GetLogger(typeof(Startup));public void Configuration(IAppBuilder app){HttpConfiguration config = new HttpConfiguration();ConfigureOAuth(app);WebApiConfig.Register(config);RouteConfig.RegisterRoutes(RouteTable.Routes);app.UseWebApi(config);Database.SetInitializer(new configuration());}private readonly ConcurrentDictionary<string, string> _authenticationCodes = new ConcurrentDictionary<string, string>(StringComparer.Ordinal);public void ConfigureOAuth(IAppBuilder app){OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions(){AllowInsecureHttp = true,TokenEndpointPath = new PathString("/token"),AuthorizeEndpointPath = new PathString("/OAuth/Authorize"),AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),Provider = new SimpleAuthorizationServerProvider(),RefreshTokenProvider = new SimpleRefreshTokenProvider(),AuthorizationCodeProvider= new AuthenticationTokenProvider(){OnCreate = CreateAuthenticationCode,OnReceive = ReceiveAuthenticationCode}};app.UseOAuthAuthorizationServer(OAuthServerOptions);app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());}private void CreateAuthenticationCode(AuthenticationTokenCreateContext context){// context.SetToken(Guid.NewGuid().ToString("n") + Guid.NewGuid().ToString("n"));context.SetToken("123");_authenticationCodes[context.Token] = context.SerializeTicket();Log.Info("Create a token with value: 123");}private void ReceiveAuthenticationCode(AuthenticationTokenReceiveContext context){string value;if(_authenticationCodes.TryRemove(context.Token,out value)){context.DeserializeTicket(value);Log.Info("Call at ReceiveAuthenticationCode");}}}}
SimpleAuthorizationServerProvider.cs
using System;using System.Collections.Generic;using System.Linq;using System.Web;using Microsoft.Owin.Security;using Microsoft.Owin.Security.OAuth;using System.Threading.Tasks;using angularjsAuthentication.api.Entities;using angularjsAuthentication.api.DataRepository;using Microsoft.AspNet.Identity.EntityFramework;using System.Security.Claims;using log4net;namespace angularjsAuthentication.api.Providers{public class SimpleAuthorizationServerProvider:OAuthAuthorizationServerProvider{private static ILog Log = LogManager.GetLogger(typeof(SimpleAuthorizationServerProvider));public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context){string clientId = string.Empty;string clientSecret = string.Empty;if(!context.TryGetBasicCredentials(out clientId,out clientSecret)){context.TryGetFormCredentials(out clientId, out clientSecret);}Log.InfoFormat("SimpleAuthorizationServerProvider.ValidateClientAuthentication, client id: {0},client secrect {1}", clientId, clientSecret);context.Validated();retu Task.FromResult<object>(null);//if (clientId==null)//{//context.SetError("invalid_clientId", "clientId should be present.");//retu Task.FromResult<object>(null);//}//using (AuthRepository _repo = new AuthRepository())//{//client = _repo.FindClient(clientId);//}//if(client==null)//{//context.SetError("invalid_clientId", string.Format("Client '{0}' is not registered in the system.", context.ClientId));//}//if(client.ApplicatonType==Models.ApplicationTypes.NativeConfidential)//{//if(string.IsNullOrWhiteSpace(clientSecret))//{//context.SetError("invalid_clientId", "Client secret should be sent.");//retu Task.FromResult<object>(null);//}//else//{//if(client.secrect!=Util.Help.GetHash(clientSecret))//{//context.SetError("invalid_clientId", "Client is inactive.");//retu Task.FromResult<object>(null);//}//}//}//if(!client.Active)//{//context.SetError("invalid_clientId", "Client is inactive.");//retu Task.FromResult<object>(null);//}//context.OwinContext.Set<string>("as:clientAllowedOrigin", client.AllowedOrigin);//context.OwinContext.Set<string>("as:clientRefreshTokenLifeTime", client.RefreshTokenLiftTime.ToString());//context.Validated();//retu Task.FromResult<object>(null);}public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context){var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");if (allowedOrigin == null) allowedOrigin = "*";context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });//using (AuthRepository _repo = new AuthRepository())//{//IdentityUser user = await _repo.FindUser(context.UserName, context.Password);//if(user==null)//{//context.SetError("invalid_grant", "The user name of password is incorrect");//retu;//}//}Log.InfoFormat("SimpleAuthorizationServerProvider.GrantResourceOwnerCredentials with userName: {0}, Password: {1}", context.UserName, context.Password);var identity = new ClaimsIdentity(context.Options.AuthenticationType);identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));identity.AddClaim(new Claim(ClaimTypes.Role, "user"));identity.AddClaim(new Claim("sub", context.UserName));var props = new AuthenticationProperties(new Dictionary<string, string> {{"as:client_id",(context.ClientId==null)?string.Empty:context.ClientId},{"userName",context.UserName}});var ticket = new AuthenticationTicket(identity, props);context.Validated(ticket);retu Task.FromResult<object>(null);}public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context){Log.InfoFormat("Call at SimpleAuthorizationServerProvider.ValidateClientRedirectUri");context.Validated("https://www.getpostman.com/oauth2/callback");retu Task.FromResult<object>(null);}public override Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context){Log.InfoFormat("grant the authorization directly in AuthorizeEndpoint");//var identity = new ClaimsIdentity(context.Options.AuthenticationType);//identity.AddClaim(new Claim(ClaimTypes.Name, "Name"));//identity.AddClaim(new Claim(ClaimTypes.Role, "user"));//identity.AddClaim(new Claim("sub", "sub"));//context.OwinContext.Authentication.SignIn(identity);Log.Info("grant success");retu Task.FromResult<object>(null);}public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context){var originalClient = context.Ticket.Properties.Dictionary["as:client_id"];var currentClient = context.ClientId;if(originalClient!=currentClient){context.SetError("invalid_clientId", "Refresh token is issued to a different clientId");retu Task.FromResult<object>(null);}var newIdentity = new ClaimsIdentity(context.Ticket.Identity);var newClaim = newIdentity.Claims.FirstOrDefault(c => c.Type == "newClaim");if(newClaim!=null){newIdentity.RemoveClaim(newClaim);}newIdentity.AddClaim(new Claim("newClaim", "newValue"));var newTicket = new AuthenticationTicket(newIdentity, context.Ticket.Properties);context.Validated(newTicket);retu Task.FromResult<object>(null);}public override Task TokenEndpoint(OAuthTokenEndpointContext context){context.Properties.Dictionary.ToList().ForEach(e =>{context.AdditionalResponseParameters.Add(e.Key, e.Value);});retu Task.FromResult<object>(null);}public override Task GrantAuthorizationCode(OAuthGrantAuthorizationCodeContext context){Log.Info("Call in GrantAuthorizationCode");var ticket = context.Ticket;context.Validated(ticket);retu Task.FromResult<object>(null);}}}
OAuthrize.cs
using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;using System.Security.Claims;using log4net;namespace angularjsAuthentication.api.Controllers.OAuth{public class OAuthController : Controller{private static ILog Log = LogManager.GetLogger(typeof(OAuthController));// GET: OAuthpublic ActionResult Index(){retu View();}public ActionResult Authorize(){var authentication = HttpContext.GetOwinContext().Authentication;if (Request.HttpMethod == "POST"){ClaimsIdentity identity = new ClaimsIdentity("Bearer", "myOAuthNameClaim", "myOAuthRoleClaim");identity.AddClaim(new Claim("Scope", "Name"));authentication.SignIn(identity);}Log.Info("Call at OAuthController.Authorize, create Identity with authentication type: myOAuthAuthentication, Name Claim: myOAuthNameClaim, Role Claim: myOAuthRoleClaim ");retu View("Authroze");}}}
SimpleRefreshTokenProvider.cs
using System;using System.Collections.Generic;using System.Linq;using System.Web;using angularjsAuthentication.api.Entities;using Microsoft.Owin.Security;using Microsoft.Owin.Security.Infrastructure;using System.Threading.Tasks;using angularjsAuthentication.api.DataRepository;using angularjsAuthentication.api.Util;using System.Collections.Concurrent;namespace angularjsAuthentication.api.Providers{public class SimpleRefreshTokenProvider:IAuthenticationTokenProvider{private readonly ConcurrentDictionary<string, string> _authenticationCodes = new ConcurrentDictionary<string, string>(StringComparer.Ordinal);public Task CreateAsync(AuthenticationTokenCreateContext context){//var clientId = context.Ticket.Properties.Dictionary["as:client_id"];//if (string.IsNullOrEmpty(clientId))//retu;var refreshTokenId = Guid.NewGuid().ToString("n");var token = new RefreshToken(){Id = Help.GetHash(refreshTokenId),ClientId = "testClient",Subject = context.Ticket.Identity.Name,IssuedUtc = DateTime.UtcNow,ExpireUtc = DateTime.UtcNow.AddMinutes(100)};context.Ticket.Properties.IssuedUtc = token.IssuedUtc;context.Ticket.Properties.ExpiresUtc = token.ExpireUtc;token.ProtectedTicket = context.SerializeTicket();_authenticationCodes[token.Id] = token.ProtectedTicket;context.SetToken(refreshTokenId);retu Task.FromResult<object>(null);//using (AuthRepository _repo = new AuthRepository())//{//var refreshTokenLifetime = context.OwinContext.Get<string>("as:clientRefreshTokenLifeTime");//var token = new RefreshToken()//{//Id = Help.GetHash(refreshTokenId),//ClientId = clientId,//Subject = context.Ticket.Identity.Name,//IssuedUtc = DateTime.UtcNow,//ExpireUtc = DateTime.UtcNow.AddMinutes(Convert.ToDouble(refreshTokenLifetime))//};//context.Ticket.Properties.IssuedUtc = token.IssuedUtc;//context.Ticket.Properties.ExpiresUtc = token.ExpireUtc;//token.ProtectedTicket = context.SerializeTicket();//var result = await _repo.AddRefreshTokenAsync(token);//if(result)//{//context.SetToken(refreshTokenId);//}//}}public Task ReceiveAsync(AuthenticationTokenReceiveContext context){//var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");//context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });string hashedTokenId = Help.GetHash(context.Token);string value;if (_authenticationCodes.TryRemove(hashedTokenId, out value)){context.DeserializeTicket(value);}retu Task.FromResult<object>(null);//using (AuthRepository _repo = new AuthRepository())//{//var refreshToken = await _repo.FindRefreshTokenAsync(hashedTokenId);//if(refreshToken!=null)//{//context.DeserializeTicket(refreshToken.ProtectedTicket);//var result = await _repo.RemoveRefreshTokenAsync(hashedTokenId);//}//}}public void Create(AuthenticationTokenCreateContext context){var clientId = context.Ticket.Properties.Dictionary["as:client_id"];if (string.IsNullOrEmpty(clientId))retu;var refreshTokenId = Guid.NewGuid().ToString("n");using (AuthRepository _repo = new AuthRepository()){var refreshTokenLifeTime = context.OwinContext.Get<string>("as:clientRefreshTokenLifeTime");var token = new RefreshToken(){Id = Help.GetHash(refreshTokenId),ClientId = clientId,Subject = context.Ticket.Identity.Name,IssuedUtc = DateTime.UtcNow,ExpireUtc = DateTime.UtcNow.AddMinutes(Convert.ToDouble(refreshTokenLifeTime))};context.Ticket.Properties.IssuedUtc = token.IssuedUtc;context.Ticket.Properties.ExpiresUtc = token.ExpireUtc;token.ProtectedTicket = context.SerializeTicket();var result = _repo.AddRefreshToken(token);if (result)context.SetToken(refreshTokenId);}}public void Receive(AuthenticationTokenReceiveContext context){var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });string hashedTokenId = Help.GetHash(context.Token);using (AuthRepository _repo = new AuthRepository()){_repo.RemoveRefreshToken(hashedTokenId);}}}}
**In the OAuthrize Control, the key function call is below. Bearer is necessary.
ClaimsIdentity identity = new ClaimsIdentity("Bearer", "myOAuthNameClaim", "myOAuthRoleClaim");identity.AddClaim(new Claim("Scope", "Name"));authentication.SignIn(identity);
作者:kongshu
来源链接:https://www.cnblogs.com/kongshu-612/p/6886039.html
版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。
2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。