{"id":289764,"date":"2018-09-22T22:10:03","date_gmt":"2018-09-22T18:10:03","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=289764"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=289764","title":{"rendered":"OAuth Authorization Provider \u0434\u043b\u044f asp net core"},"content":{"rendered":"\n<div class=\"post__text post__text-html js-mediator-article\">\u041d\u0430\u0447\u0430\u043b \u0438\u0437\u0443\u0447\u0430\u0442\u044c asp.net core \u0438 \u043f\u0435\u0440\u0432\u043e\u0435 \u0447\u0442\u043e \u043f\u044b\u0442\u0430\u043b\u0441\u044f \u043d\u0430\u0439\u0442\u0438 \u044d\u0442\u043e \u043d\u0435\u043a\u043e\u0435 \u043f\u043e\u0434\u043e\u0431\u0438\u0435 \u00abOAuthAuthorizationServerProvider\u00bb \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0442\u0438\u043a\u0435\u0442\u0430 \u0438 \u00abIAuthenticationTokenProvider\u00bb \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439 \u0440\u0435\u0444\u0440\u0435\u0448 \u0442\u043e\u043a\u0435\u043d\u0430 \u043a\u0430\u043a \u0432 \u043e\u0431\u044b\u0447\u043d\u043e\u043c asp.net, \u043d\u043e \u043d\u0435 \u043d\u0430\u0448\u0435\u043b. \u041d\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u043e, \u0447\u0442\u043e \u043f\u043b\u043e\u0445\u043e \u0438\u0441\u043a\u0430\u043b, \u0438 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f \u043a\u043e\u043c\u043c\u0435\u043d\u0442 \u0442\u0438\u043f\u0430 \u00ab\u0432\u043e\u0442 \u043e\u0431\u043a\u0430\u0442\u0430\u043d\u043d\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0434\u0435\u043b\u0430\u00bb.<\/p>\n<p>  \u0425\u043e\u0440\u043e\u0448\u043e, \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0439 Middleware \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u0438 \u0441\u0432\u044f\u0437\u0430\u0442\u044c \u0435\u0433\u043e \u0441 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u0447\u0435\u0440\u0435\u0437 \u00ab\u0441\u0435\u0440\u0432\u0438\u0441\u044b\u00bb \u0432 ConfigureServices.<\/p>\n<p>  \u0418\u0442\u0430\u043a, \u043c\u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0447\u0442\u043e \u0431\u044b \u0431\u044b\u043b\u0438 \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0442\u043e\u043a\u0435\u043d\u0430 \u043f\u043e \u043b\u043e\u0433\u0438\u043d\u0443 \u0438 \u043f\u0430\u0440\u043e\u043b\u044e \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 &#171;\/token&#187; \u0438 \u043c\u0435\u0442\u043e\u0434 \u043f\u043e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044e \u0442\u043e\u043a\u0435\u043d\u0430 \u043f\u043e \u0440\u0435\u0444\u0440\u0435\u0448-\u0442\u043e\u043a\u0435\u043d\u0443.<\/p>\n<pre><code class=\"cs\">   public interface IOAuthProvider     {         Task ByPassword(OAuthProviderContext oAuthProviderContext);         Task ByRefreshToken(OAuthProviderContext oAuthProviderContext);     }<\/code><\/pre>\n<p>  <a name=\"habracut\"><\/a><br \/>  \u0422\u043e \u0435\u0441\u0442\u044c \u0432 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u043c \u0438\u0442\u043e\u0433\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0432 \u043e\u0434\u0438\u043d \u044d\u0442\u043e\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0438 \u0441\u0432\u044f\u0437\u0430\u0432 \u0435\u0433\u043e \u0432 ConfigureServices \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0434\u043e\u043b\u0436\u043d\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c.<\/p>\n<p>  \u0412 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0435 \u00abOAuthProviderContext\u00bb \u0431\u0443\u0434\u0443\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u0434\u0430\u043d\u043d\u044b\u0435 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438:<\/p>\n<pre><code class=\"cs\">    public class OAuthProviderContext     {         public bool HasError { get; private set; }         public string Error { get; private set; }         public string AccessToken { get; private set; }         public string RefreshToken { get; set; }          public string ClientId { get; set; }         public string Username { get; set; }         public string Password { get; set; }         public void SetError(string error)         {             Error = error;             HasError = true;         }         public void SetToken(string access_token, string refresh_token)         {             AccessToken = access_token;             RefreshToken = refresh_token;         }     }<\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0434\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c middleware, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0431\u0443\u0434\u0443\u0449\u0438\u043c\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f\u043c\u0438 IOAuthProvider:<\/p>\n<pre><code class=\"cs\">class OAuthProviderMiddleware     {         RequestDelegate _next;         IOAuthProvider _oAuthProvider;                 public OAuthProviderMiddleware(RequestDelegate next, IOAuthProvider oAuthProvider)         {             _next = next;             _oAuthProvider = oAuthProvider;         }         public async Task Invoke(HttpContext context)         {             OAuthProviderContext _oAuthProviderContext;              string path = context.Request.Path.Value.ToLower().Trim();              bool isPost = context.Request.Method.ToLower() == \"post\";              if (path == \"\/token\" &amp;&amp; isPost)             {                 var form = context.Request.Form;                  if (!form.ContainsKey(\"grant_type\")) {                     await context.BadRequest(\"invalid grant_type\");                     return;                 }                 if (!form.ContainsKey(\"client_id\"))                 {                     await context.BadRequest(\"invalid client_id\");                     return;                 }                  string grant_type = form[\"grant_type\"];                 string client_id = form[\"client_id\"];                  switch (grant_type)                 {                     case \"password\":                         {                             if (!form.ContainsKey(\"username\"))                             {                                 await context.BadRequest(\"invalid username\");                                 return;                             }                             if (!form.ContainsKey(\"password\"))                             {                                 await context.BadRequest(\"invalid password\");                                 return;                             }                              string username = form[\"username\"];                             string password = form[\"password\"];                              _oAuthProviderContext = new OAuthProviderContext()                             {                                 ClientId = client_id,                                 Username = username,                                 Password = password                             };                               await _oAuthProvider.ByPassword(_oAuthProviderContext);                              if (_oAuthProviderContext.HasError)                             {                                 await context.BadRequest(_oAuthProviderContext.Error);                                 return;                             }                             else                             {                                 await context.WriteToken(_oAuthProviderContext);                                 return;                             }                          };                     case \"refresh_token\":                         {                             if (!form.ContainsKey(\"refresh_token\"))                             {                                 await context.BadRequest(\"invalid refresh_token\");                                 return;                             }                              string refresh_token = form[\"refresh_token\"];                              _oAuthProviderContext = new OAuthProviderContext()                             {                                 ClientId = client_id,                                 RefreshToken = refresh_token                             };                              await _oAuthProvider.ByRefreshToken(_oAuthProviderContext);                              if (_oAuthProviderContext.HasError)                             {                                 await context.BadRequest(_oAuthProviderContext.Error);                                                                 return;                             }                             else                             {                                 await context.WriteToken(_oAuthProviderContext);                                 return;                             }                         };                     default:                         {                             await context.BadRequest(\"invalid grant_type\");                             return;                         };                 }             }             else             {                 await _next.Invoke(context);             }         }     }      public static class OAuthExtensions     {         public static IApplicationBuilder UseOAuth(this IApplicationBuilder builder)         {             return builder.UseMiddleware&lt;OAuthProviderMiddleware&gt;();         }          internal static async Task BadRequest(this HttpContext context, string Error)         {             context.Response.StatusCode = 400;             await context.Response.WriteAsync(Error);         }          internal static async Task WriteToken(this HttpContext context, OAuthProviderContext _oAuthProviderContext)         {             context.Response.ContentType = \"application\/json\";             await context.Response.WriteAsync(JsonConvert.SerializeObject(new             {                 access_token = _oAuthProviderContext.AccessToken,                 refresh_token = _oAuthProviderContext.RefreshToken             }));         }     }<\/code><\/pre>\n<p>  \u0412 Configure \u043f\u043e\u0442\u043e\u043c \u043c\u043e\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u043e\u0431\u0435\u0440\u0442\u043a\u0443 <\/p>\n<pre><code class=\"cs\">app.UseOAuth();<\/code><\/pre>\n<p>  \u0414\u0430\u043b\u0435\u0435 \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e IOAuthProvider.<br \/>  \u0422\u043e\u043a\u0435\u043d \u0431\u0443\u0434\u0435\u0442 \u0432 \u0432\u0438\u0434\u0435 JWT, \u0430 \u0440\u0435\u0444\u0440\u0435\u0448-\u0442\u043e\u043a\u0435\u043d \u0440\u0430\u043d\u0434\u043e\u043c\u043d\u044b\u0439 byte[] \u043c\u0430\u0441\u0441\u0438\u0432 \u0434\u043b\u0438\u043d\u043e\u0439 100 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u043a\u0430\u043a Base64. \u0412 \u043a\u043e\u043d\u0446\u0435 \u0435\u0449\u0435 \u0431\u0443\u0434\u0435\u0442 \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043a\u043e\u0434.<\/p>\n<pre><code class=\"cs\"> public class OAuthProviderImplement : IOAuthProvider     {         IServiceProvider _services;         IOptions&lt;AuthOptions&gt; _authOptions = null;         Helper _helper = null;          public OAuthProviderImplement(IServiceProvider services, IOptions&lt;AuthOptions&gt; authOptions, Helper helper)         {             _services = services;             _authOptions = authOptions;             _helper = helper;         }          public async Task ByPassword(OAuthProviderContext context)         {             ClaimsIdentity identity = await GetIdentity(context.Username, context.ClientId, context.Password);             if (identity == null)             {                 context.SetError(\"User not found\");                 return;             }              string encodedJwt = CreateJWT(identity);             string refresh_token = await CreateRefreshToken(context.ClientId, identity);              if (refresh_token == null)             {                 context.SetError(\"Error while create refresh token\");                 return;             }              context.SetToken(encodedJwt, refresh_token);             return;         }          public async Task ByRefreshToken(OAuthProviderContext context)         {             ProtectedTicket protectedTicket = await GrantRefreshToken(context.RefreshToken);              if (protectedTicket == null)             {                 context.SetError(\"Invalid refresh token\");                 return;             }              if (protectedTicket.clientid != context.ClientId)             {                 context.SetError(\"Invalid client id\");                 return;             }              ClaimsIdentity identity = await GetIdentity(protectedTicket.username, protectedTicket.clientid);              if (identity == null)             {                 context.SetError(\"User not found\");                 return;             }              string encodedJwt = CreateJWT(identity);              context.SetToken(encodedJwt, context.RefreshToken);             return;         }          string CreateJWT(ClaimsIdentity identity)         {             var now = DateTime.UtcNow;             \/\/ \u0441\u043e\u0437\u0434\u0430\u0435\u043c JWT-\u0442\u043e\u043a\u0435\u043d             var jwt = new JwtSecurityToken(                     issuer: _authOptions.Value.Issuer,                     audience: _authOptions.Value.Audience,                     notBefore: now,                     claims: identity.Claims,                     expires: now.Add(TimeSpan.FromSeconds(_authOptions.Value.LifetimeSeconds)),                     signingCredentials: new SigningCredentials(_authOptions.Value.GetSymmetricSecurityKey(), SecurityAlgorithms.HmacSha256));              var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);              return encodedJwt;         }          async Task&lt;ClaimsIdentity&gt; GetIdentity(string username, string clientid, string password = null)         {             using (var serviceScope = _services.GetRequiredService&lt;IServiceScopeFactory&gt;().CreateScope())             {                 IAuthRepository _repo = serviceScope.ServiceProvider.GetService&lt;IAuthRepository&gt;();                  ApplicationUser user = null;                 if (password != null)                 {                     user = await _repo.FindUser(username, password);                 }                 else                 {                     user = await _repo.FindUser(username);                 }                  if (user != null)                 {                     var claims = new List&lt;Claim&gt;                 {                     new Claim(ClaimsIdentity.DefaultNameClaimType, user.UserName)                 };                      foreach (var r in await _repo.GetRoles(user))                     {                         claims.Add(new Claim(ClaimsIdentity.DefaultRoleClaimType, r));                     }                      claims.Add(new Claim(\"client_id\", clientid));                      ClaimsIdentity claimsIdentity = new ClaimsIdentity(                         claims,                         \"Password\",                         ClaimsIdentity.DefaultNameClaimType,                         ClaimsIdentity.DefaultRoleClaimType);                      return claimsIdentity;                 }                 else                 {                  }                  \/\/ \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e                 return null;             }         }          async Task&lt;string&gt; CreateRefreshToken(string clientid, ClaimsIdentity claimsIdentity)         {             using (var serviceScope = _services.GetRequiredService&lt;IServiceScopeFactory&gt;().CreateScope())             {                 IAuthRepository _repo = serviceScope.ServiceProvider.GetService&lt;IAuthRepository&gt;();                 Client client = _repo.FindClient(clientid);                  var refreshTokenId = _helper.GetHash(_helper.GenerateRandomCryptographicKey(100));                  var refreshTokenLifeTime = client.RefreshTokenLifeTime;                  var now = DateTime.UtcNow;                  var token = new RefreshToken()                 {                     Id = _helper.GetHash(refreshTokenId),                     ClientId = clientid,                     Subject = claimsIdentity.Name,                     IssuedUtc = now,                     ExpiresUtc = now.AddMinutes(Convert.ToDouble(refreshTokenLifeTime))                 };                  token.ProtectedTicket = JsonConvert.SerializeObject(new ProtectedTicket { clientid = clientid, username = claimsIdentity.Name });                  var result = await _repo.AddRefreshToken(token);                  if (result)                 {                     return refreshTokenId;                 }                  return null;             }         }          async Task&lt;ProtectedTicket&gt; GrantRefreshToken(string refreshTokenId)         {             using (var serviceScope = _services.GetRequiredService&lt;IServiceScopeFactory&gt;().CreateScope())             {                 IAuthRepository _repo = serviceScope.ServiceProvider.GetService&lt;IAuthRepository&gt;();                 string hashedTokenId = _helper.GetHash(refreshTokenId);                 ProtectedTicket protectedTicket = null;                  var refreshToken = await _repo.FindRefreshToken(hashedTokenId);                  if (refreshToken != null)                 {                     \/\/Get protectedTicket from refreshToken class                     protectedTicket = JsonConvert.DeserializeObject&lt;ProtectedTicket&gt;(refreshToken.ProtectedTicket);                      return protectedTicket;                 }                 else                 {                     return null;                 }             }         }     }<\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442\u0441\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u044b:<br \/>  post &#171;\/token&#187; \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u0432 body: grant_type=\u00abpassword\u00bb, client_id=\u00abngAuth\u00bb, username=\u00abadmin\u00bb, password=\u00ab123\u00bb<br \/>  \u0438<br \/>  post &#171;\/token&#187; \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u0432 body: grant_type=\u00abrefresh_token\u00bb, client_id=\u00abngAuth\u00bb, refresh_token=\u00abdgDrVQHylvHmi8QZ5oThVjWyqdrLYKhp1\/XHsIJI65g=\u00bb<\/p>\n<p>  \u041d\u0430 \u044d\u0442\u043e\u043c \u0432\u0441\u0435, \u0432\u043e\u043e\u0431\u0449\u0435\u043c \u043d\u0430\u043f\u0438\u0448\u0438\u0442\u0435 \u043a\u0442\u043e \u0447\u0435 \u0434\u0443\u043c\u0430\u0435\u0442.<\/p>\n<p>  <a href=\"https:\/\/github.com\/AscarGb\/TestAspNetCore\">\u0412\u0435\u0441\u044c \u043a\u043e\u0434<\/a><\/div>\n<p>        <script class=\"js-mediator-script\">!function(e){function t(t,n){if(!(n in e)){for(var r,a=e.document,i=a.scripts,o=i.length;o--;)if(-1!==i[o].src.indexOf(t)){r=i[o];break}if(!r){r=a.createElement(\"script\"),r.type=\"text\/javascript\",r.async=!0,r.defer=!0,r.src=t,r.charset=\"UTF-8\";var d=function(){var e=a.getElementsByTagName(\"script\")[0];e.parentNode.insertBefore(r,e)};\"[object Opera]\"==e.opera?a.addEventListener?a.addEventListener(\"DOMContentLoaded\",d,!1):e.attachEvent(\"onload\",d):d()}}}t(\"\/\/mediator.mail.ru\/script\/2820404\/\",\"_mediator\")}(window);<\/script>     <br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/post\/424101\/\"> https:\/\/habr.com\/post\/424101\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text-html js-mediator-article\">\u041d\u0430\u0447\u0430\u043b \u0438\u0437\u0443\u0447\u0430\u0442\u044c asp.net core \u0438 \u043f\u0435\u0440\u0432\u043e\u0435 \u0447\u0442\u043e \u043f\u044b\u0442\u0430\u043b\u0441\u044f \u043d\u0430\u0439\u0442\u0438 \u044d\u0442\u043e \u043d\u0435\u043a\u043e\u0435 \u043f\u043e\u0434\u043e\u0431\u0438\u0435 \u00abOAuthAuthorizationServerProvider\u00bb \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0442\u0438\u043a\u0435\u0442\u0430 \u0438 \u00abIAuthenticationTokenProvider\u00bb \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439 \u0440\u0435\u0444\u0440\u0435\u0448 \u0442\u043e\u043a\u0435\u043d\u0430 \u043a\u0430\u043a \u0432 \u043e\u0431\u044b\u0447\u043d\u043e\u043c asp.net, \u043d\u043e \u043d\u0435 \u043d\u0430\u0448\u0435\u043b. \u041d\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u043e, \u0447\u0442\u043e \u043f\u043b\u043e\u0445\u043e \u0438\u0441\u043a\u0430\u043b, \u0438 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f \u043a\u043e\u043c\u043c\u0435\u043d\u0442 \u0442\u0438\u043f\u0430 \u00ab\u0432\u043e\u0442 \u043e\u0431\u043a\u0430\u0442\u0430\u043d\u043d\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0434\u0435\u043b\u0430\u00bb.<\/p>\n<p>  \u0425\u043e\u0440\u043e\u0448\u043e, \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0439 Middleware \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u0438 \u0441\u0432\u044f\u0437\u0430\u0442\u044c \u0435\u0433\u043e \u0441 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u0447\u0435\u0440\u0435\u0437 \u00ab\u0441\u0435\u0440\u0432\u0438\u0441\u044b\u00bb \u0432 ConfigureServices.<\/p>\n<p>  \u0418\u0442\u0430\u043a, \u043c\u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0447\u0442\u043e \u0431\u044b \u0431\u044b\u043b\u0438 \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0442\u043e\u043a\u0435\u043d\u0430 \u043f\u043e \u043b\u043e\u0433\u0438\u043d\u0443 \u0438 \u043f\u0430\u0440\u043e\u043b\u044e \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 &#171;\/token&#187; \u0438 \u043c\u0435\u0442\u043e\u0434 \u043f\u043e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044e \u0442\u043e\u043a\u0435\u043d\u0430 \u043f\u043e \u0440\u0435\u0444\u0440\u0435\u0448-\u0442\u043e\u043a\u0435\u043d\u0443.<\/p>\n<pre><code class=\"cs\">   public interface IOAuthProvider     {         Task ByPassword(OAuthProviderContext oAuthProviderContext);         Task ByRefreshToken(OAuthProviderContext oAuthProviderContext);     }<\/code><\/pre>\n<p>  <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-289764","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/289764","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=289764"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/289764\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=289764"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=289764"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=289764"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}