OWIN AuthenticationOptions在mvc5应用程序在运行时更新 [英] OWIN AuthenticationOptions updating at runtime in mvc5 application

查看:775
本文介绍了OWIN AuthenticationOptions在mvc5应用程序在运行时更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述




这里的情况是:结果
我有IIS7至极与Identity2 1 MVC5应用提供多个网站。
主机名是某些网站的关键。结果
site.com,
anothersite.com



我决定在我所有的网站使用外部登录谷歌和每个站点应该是谷歌的客户个人客户端ID和clientsecret结果
为例:结果
网站。 COM - 客户端ID = 123123,clientsecret = xxxaaabbb结果
anothersite.com - 客户端ID = 890890,clientsecret = zzzqqqeee



但有一个小问题 -
AuthenticationOptions 设定在应用程序启动和我did'n找到任何方式在运行时更换。



所以,在阅读的创建自定义的OAuth中间件MVC 5
和的编写一个Owin认证中间件
我已经意识到我应该重写 AuthenticationHandler.ApplyResponseChallengeAsync()
,并把这段代码在这个方法的开头:



<预类=郎-CS prettyprint-覆盖> Options.ClientId = OAuth2Helper.GetProviderAppId( 谷歌);
Options.ClientSecret = OAuth2Helper.GetProviderAppSecret(谷歌);



我决定只使用Google,因此,我们将谈论谷歌中间件。




  1. 的AuthenticationHandler 返回AuthenticationMiddleWare.CreateHandler()在我的情况下,他们是 GoogleOAuth2AuthenticationHandler GoogleOAuth2AuthenticationMiddleware 。结果
    我已经找到 GoogleOAuth2AuthenticationMiddleware 在的 http://katanaproject.codeplex.com/
    ,并把它在我的项目是这样



    <预类= 郎咸平-CS prettyprint-覆盖> 公共类GoogleAuthenticationMiddlewareExtended:GoogleOAuth2AuthenticationMiddleware
    {
    私人只读ILogger _logger;
    私人只读的HttpClient _httpClient;

    公共GoogleAuthenticationMiddlewareExtended(
    OwinMiddleware接下来,
    IAppBuilder应用程序,
    GoogleOAuth2AuthenticationOptions选项)
    :基地(接下来,应用程序,期权)
    {
    _logger = app.CreateLogger< GoogleOAuth2AuthenticationMiddleware>();
    _httpClient =新的HttpClient(ResolveHttpMessageHandler(选项));
    _httpClient.Timeout = Options.BackchannelTimeout;
    _httpClient.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB
    }

    保护覆盖的AuthenticationHandler< GoogleOAuth2AuthenticationOptions> CreateHandler()
    {
    返回新GoogleOAuth2AuthenticationHandlerExtended(_httpClient,_logger);
    }

    私有静态HttpMessageHandler ResolveHttpMessageHandler(GoogleOAuth2AuthenticationOptions选项)
    {
    HttpMessageHandler处理器= options.BackchannelHttpHandler?新WebRequestHandler();

    //如果他们提供了一个验证器,应用它还是失败。
    如果(options.BackchannelCertificateValidator!= NULL)
    {
    //设置证书验证回调
    VAR webRequestHandler =处理程序WebRequestHandler;
    如果(webRequestHandler == NULL)
    {
    抛出新的InvalidOperationException异常(Exception_ValidatorHandlerMismatch);
    }
    webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate;
    }
    返回处理程序;
    }
    }


  2. 然后我已经创建自己的处理程序改性ApplyResponseChallengeAsync。我得在这一点上的坏消息 - GoogleOAuth2AuthenticationHandler 是内部的,我不得不把它完全,并把在我的项目像这样的(同样的 katanaproject.codeplex.com



    <预类=郎-CS prettyprint-覆盖> 公共类GoogleOAuth2AuthenticationHandlerExtended:&的AuthenticationHandler LT; GoogleOAuth2AuthenticationOptions>
    {
    私人常量字符串TokenEndpoint =https://accounts.google.com/o/oauth2/token;
    私人常量字符串UserInfoEndpoint =https://www.googleapis.com/oauth2/v3/userinfo?access_token=;
    私人常量字符串AuthorizeEndpoint =https://accounts.google.com/o/oauth2/auth;

    私人只读ILogger _logger;
    私人只读的HttpClient _httpClient;

    公共GoogleOAuth2AuthenticationHandlerExtended(HttpClient的HttpClient的,ILogger记录器)
    {
    _httpClient = HttpClient的;
    _logger =记录仪;
    }

    //我有一些surpises这里
    保护覆盖异步任务< AuthenticationTicket> AuthenticateCoreAsync()
    {
    AuthenticationProperties性能= NULL;


    {
    串码= NULL;
    串状态= NULL;

    IReadableStringCollection查询= Request.Query;
    &IList的LT;字符串>值= query.GetValues(准则);
    如果(值=空&放大器;!&放大器; values.Count == 1)
    {
    码=值[0];
    }
    值= query.GetValues(国家);
    如果(值= NULL&放大器;!&安培; values.Count == 1)
    {
    状态=值[0];
    }

    性能= Options.StateDataFormat.Unprotect(州);
    如果(属性== NULL)
    {
    返回NULL; (!ValidateCorrelationId(属性,_logger))
    }

    //的OAuth2 10.12 CSRF
    如果
    {
    返回新AuthenticationTicket(NULL,属性);
    }

    串requestPrefix = Request.Scheme +://+ Request.Host;
    串redirectUri = requestPrefix + Request.PathBase + Options.CallbackPath;

    //增进身体的健康为令牌请求
    变种体=新的List< KeyValuePair<字符串,字符串>>();
    body.Add(新KeyValuePair<字符串,字符串>(grant_type,authorization_code));
    body.Add(新KeyValuePair<字符串,字符串>(代码,代码));
    body.Add(新KeyValuePair<字符串,字符串>(REDIRECT_URI,redirectUri));
    body.Add(新KeyValuePair<字符串,字符串>(CLIENT_ID,Options.ClientId));
    body.Add(新KeyValuePair<字符串,字符串>(client_secret,Options.ClientSecret));

    //请求令牌
    HttpResponseMessage tokenResponse =
    等待_httpClient.PostAsync(TokenEndpoint,新FormUrlEncodedContent(体));
    tokenResponse.EnsureSuccessStatusCode();
    字符串文本=等待tokenResponse.Content.ReadAsStringAsync();

    //反序列化令牌响应
    JObject响应= JObject.Parse(文本);
    串的accessToken = response.Value<串GT(ACCESS_TOKEN);
    串到期= response.Value<串GT(expires_in);
    串refreshToken = response.Value<串GT(refresh_token);

    如果(string.IsNullOrWhiteSpace(的accessToken))
    {
    _logger.WriteWarning(访问令牌未找到);
    返回新AuthenticationTicket(NULL,属性);
    }

    //获取谷歌用户
    HttpResponseMessage graphResponse =等待_httpClient.GetAsync(
    UserInfoEndpoint + Uri.EscapeDataString(的accessToken),Request.CallCancelled);
    graphResponse.EnsureSuccessStatusCode();

    //我将显示该内容VAR以后
    文本=等待graphResponse.Content.ReadAsStringAsync();
    JObject用户= JObject.Parse(文本);


    //因为GoogleOAuth2AuthenticatedContext构造永久性例外。我准备的用户数据与我的扩展
    JObject correctUser = OAuth2Helper.PrepareGoogleUserInfo(用户);

    //我和selfprepared user2的
    // VAR背景下更换此=新GoogleOAuth2AuthenticatedContext(背景下,用户的accessToken,refreshToken,到期);
    VAR背景=新GoogleOAuth2AuthenticatedContext(上下文,correctUser,的accessToken,refreshToken,到期);
    context.Identity =新ClaimsIdentity(
    Options.AuthenticationType,
    ClaimsIdentity.DefaultNameClaimType,
    ClaimsIdentity.DefaultRoleClaimType);
    如果(!string.IsNullOrEmpty(context.Id))
    {
    context.Identity.AddClaim(新索赔(ClaimTypes.NameIdentifier,context.Id,
    ClaimValueTypes.String, Options.AuthenticationType));
    }
    如果(!string.IsNullOrEmpty(context.GivenName))
    {
    context.Identity.AddClaim(新索赔(ClaimTypes.GivenName,context.GivenName,
    ClaimValueTypes.String,Options.AuthenticationType));
    }
    如果(!string.IsNullOrEmpty(context.FamilyName))
    {
    context.Identity.AddClaim(新索赔(ClaimTypes.Surname,context.FamilyName,
    ClaimValueTypes.String,Options.AuthenticationType));
    }
    如果(!string.IsNullOrEmpty(context.Name))
    {
    context.Identity.AddClaim(新索赔(ClaimTypes.Name,context.Name,ClaimValueTypes.String ,
    Options.AuthenticationType));
    }
    如果(!string.IsNullOrEmpty(context.Email))
    {
    context.Identity.AddClaim(新索赔(ClaimTypes.Email,context.Email,ClaimValueTypes.String ,
    Options.AuthenticationType));
    }

    如果(string.IsNullOrEmpty(context.Profile)!)
    {
    context.Identity.AddClaim(新索赔(瓮:谷歌:配置文件 ,context.Profile,ClaimValueTypes.String,
    Options.AuthenticationType));
    }
    context.Properties =性能;

    伺机Options.Provider.Authenticated(背景);

    返回新AuthenticationTicket(context.Identity,context.Properties);
    }
    赶上(异常前)
    {
    _logger.WriteError(验证失败,前);
    返回新AuthenticationTicket(NULL,属性);
    }
    }

    保护覆盖任务ApplyResponseChallengeAsync()
    {

    // finaly!这里是。我只是想在这里把这个两行。多数民众赞成
    Options.ClientId = OAuth2Helper.GetProviderAppId(谷歌);
    Options.ClientSecret = OAuth2Helper.GetProviderAppSecret(谷歌);

    / *默认代码加时赛方法* /
    }

    //没有改变
    公众覆盖异步任务<布尔> InvokeAsync()
    {
    / *默认代码这里* /
    }

    //没有改变
    私人异步任务<布尔> InvokeReplyPathAsync()
    {
    / *默认代码这里* /
    }

    //没有改变
    私有静态无效AddQueryString(IDictionary的<字符串,字符串> ;查询字符串,AuthenticationProperties性质,
    字符串名称,字符串设置defaultValue = NULL)
    {
    / *默认位置代码* /
    }
    }




毕竟我得到一些惊喜。




  1. 为myhost /登入,谷歌之后,我得到
    为myhost /帐号/ ExternalLoginCallback?错误= ACCESS_DENIED
    和302重定向回登录页面没有成功。结果
    ,这是因为在 GoogleOAuth2AuthenticatedContext 构造的内部方法少数例外的。



    <预类=郎-CS prettyprint-覆盖> 给定名称= TryGetValue(用户,姓名,给定名称);
    FamilyName = TryGetValue(用户,姓名,familyName);






<预类=郎-CS prettyprint-覆盖> 电子邮件= TryGetFirstValue(用户,电子邮件,值);

和这里是我们翻译成 JObject用户的谷歌回应



<预类=郎-CS prettyprint-覆盖> {
分:XXXXXXXXXXXXXXXXXX,
名:约翰·史密斯,
GIVEN_NAME:约翰,
FAMILY_NAME:史密斯,
简介:https://开头加。 google.com/XXXXXXXXXXXXXXXXXX,
图片报:https://lh5.googleusercontent.com/url-to-the-picture/photo.jpg,
电子邮件:usermail @ domain.com,
email_verified:真实,
性别:男,
语言环境:汝,
HD:谷歌应用程序网站
}

名称是字符串, TryGetValue(用户,姓名,给定名称)将失败,因为 TryGetValue(用户,姓名,familyName)结果
电子邮件错过



这就是为什么我使用的辅助至极翻译用户纠正correctUser




  • correctUser是确定,但我仍然没有成功。为什么?
    为myhost /登入,谷歌之后,我得到
    为myhost /帐号/ ExternalLoginCallback
    和302重定向回,但没有成功登录页面。



  • ID 在谷歌的反应是actualy 所以结果
    •AuthenticatedContext的Id属性不填充结果
    ClaimTypes.NameIdentifier 从未创建结果
    •AccountController.ExternalLoginCallback(字符串RETURNURL)会始终重定向我们,因为LOGININFO为null



    GetExternalLoginInfo需要AuthenticateResult至极,不应为null
    和它检查 result.Identity 为ClaimTypes.NameIdentifier存在



    改名 ID 做的工作。
    现在一切正常。



    看来微软执行武士刀从卡塔纳来源
    不同,因为如果我使用默认的一切工作,没有任何魔法



    如果你可以纠正我,如果你知道更多最简单的方法基于主机名,使owin工作,在运行时确定AuthenticationOptions,请告诉我。


    解决方案

    我最近试图让多tennancy具有相同的OAuth提供者,但不同的帐户工作作战。我知道你想在运行时动态更新的选项,但你可能并不需要做的,希望这可以帮助...



    我认为你做的不是这个原因有这方面的工作,甚至重写所有这些类别是因为每一个配置的谷歌帐户的OAuth需要有一个独特的CallbackPath。这是确定哪些登记的供应商和选项将执行的回调。



    而不是试图动态地做到这一点,你可以在启动时申报每个OAuth的供应商,并确保他们有独特的AuthenticationType和独特的CallbackPath,例如:

      //供应商#1 
    app.UseGoogleAuthentication(新GoogleOAuth2AuthenticationOptions
    {
    AuthenticationType =Google-Site.Com,
    客户端Id =ABCDEF ...,
    ClientSecret =zyxwv ......,
    CallbackPath =新PathString(/ SITECOM式登入 - 谷歌)
    });

    //提供商#2
    app.UseGoogleAuthentication(新GoogleOAuth2AuthenticationOptions
    {
    AuthenticationType =Google-AnotherSite.com,
    客户端Id =ABCDEF ...,
    ClientSecret =zyxwv ......,
    CallbackPath =新PathString(/ anothersitecom式登入 - 谷歌)
    });



    然后,你在哪里调用 IOwinContext.Authentication.Challenge 您一定要通过它您正确地命名为AuthenticationType要验证目前的租户。例: HttpContext.GetOwinContext()Authentication.Challenge(属性,Google-AnotherSite.com);



    下一步是更新谷歌的开发者控制台回调的路径,以匹配您的自定义回调的路径。默认情况下它是登入,谷歌,但每一项需求,让供应商知道它需要处理路径上的特定回调是你的声明供应商中是独一无二的。



    我其实只是在这里进行更详细的博客上讲述了这一切:的 http://shazwazza.com/post/configuring-aspnet-identity-oauth-login-providers-for-multi-tenancy/


    Hi!

    Here is the situation:
    I have one MVC5 application with Identity2 on iis7 wich serves multiple web sites. host name is the key for certain web site.
    site.com, anothersite.com and so on

    i've decided to use external login with google on all my sites and every site should be google client with personal clientid and clientsecret.
    for example:
    site.com - clientid=123123, clientsecret=xxxaaabbb
    anothersite.com - clientid=890890, clientsecret=zzzqqqeee

    but there is a little problem -- AuthenticationOptions are set at the start of application and i did'n find any way to replace it at runtime.

    so, after reading Creating Custom OAuth Middleware for MVC 5 and Writing an Owin Authentication Middleware i've realized that i should override AuthenticationHandler.ApplyResponseChallengeAsync() and put this piece of code in the begining of this method:

        Options.ClientId = OAuth2Helper.GetProviderAppId("google");
        Options.ClientSecret = OAuth2Helper.GetProviderAppSecret("google");
    

    i've decided to use only google, so we will talk about google middleware.

    1. AuthenticationHandler are returned by AuthenticationMiddleWare.CreateHandler() and in my case they are GoogleOAuth2AuthenticationHandler and GoogleOAuth2AuthenticationMiddleware.
      I've found GoogleOAuth2AuthenticationMiddleware at the http://katanaproject.codeplex.com/ and take it in my project like this

      public class GoogleAuthenticationMiddlewareExtended : GoogleOAuth2AuthenticationMiddleware
      {
          private readonly ILogger _logger;
          private readonly HttpClient _httpClient;
      
          public GoogleAuthenticationMiddlewareExtended(
              OwinMiddleware next,
              IAppBuilder app,
              GoogleOAuth2AuthenticationOptions options)
              : base(next, app, options)
          {
              _logger = app.CreateLogger<GoogleOAuth2AuthenticationMiddleware>();
              _httpClient = new HttpClient(ResolveHttpMessageHandler(Options));
              _httpClient.Timeout = Options.BackchannelTimeout;
              _httpClient.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB
          }
      
          protected override AuthenticationHandler<GoogleOAuth2AuthenticationOptions> CreateHandler()
          {
              return new GoogleOAuth2AuthenticationHandlerExtended(_httpClient, _logger);
          }
      
          private static HttpMessageHandler ResolveHttpMessageHandler(GoogleOAuth2AuthenticationOptions options)
          {
              HttpMessageHandler handler = options.BackchannelHttpHandler ?? new WebRequestHandler();
      
              // If they provided a validator, apply it or fail.
              if (options.BackchannelCertificateValidator != null)
              {
                  // Set the cert validate callback
                  var webRequestHandler = handler as WebRequestHandler;
                  if (webRequestHandler == null)
                  {
                      throw new InvalidOperationException("Exception_ValidatorHandlerMismatch");
                  }
                  webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate;
              }
              return handler;
          }
      }
      

    2. then i've create my own Handler with modified ApplyResponseChallengeAsync. i've got a bad news at this point - GoogleOAuth2AuthenticationHandler is internal and i had to take it entirely and put in my project like this (again katanaproject.codeplex.com)

      public class GoogleOAuth2AuthenticationHandlerExtended : AuthenticationHandler<GoogleOAuth2AuthenticationOptions>
      {
          private const string TokenEndpoint = "https://accounts.google.com/o/oauth2/token";
          private const string UserInfoEndpoint = "https://www.googleapis.com/oauth2/v3/userinfo?access_token=";
          private const string AuthorizeEndpoint = "https://accounts.google.com/o/oauth2/auth";
      
          private readonly ILogger _logger;
          private readonly HttpClient _httpClient;
      
          public GoogleOAuth2AuthenticationHandlerExtended(HttpClient httpClient, ILogger logger)
          {
              _httpClient = httpClient;
              _logger = logger;
          }
      
          // i've got some surpises here
          protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
          {
              AuthenticationProperties properties = null;
      
              try
              {
                  string code = null;
                  string state = null;
      
                  IReadableStringCollection query = Request.Query;
                  IList<string> values = query.GetValues("code");
                  if (values != null && values.Count == 1)
                  {
                      code = values[0];
                  }
                  values = query.GetValues("state");
                  if (values != null && values.Count == 1)
                  {
                      state = values[0];
                  }
      
                  properties = Options.StateDataFormat.Unprotect(state);
                  if (properties == null)
                  {
                      return null;
                  }
      
                  // OAuth2 10.12 CSRF
                  if (!ValidateCorrelationId(properties, _logger))
                  {
                      return new AuthenticationTicket(null, properties);
                  }
      
                  string requestPrefix = Request.Scheme + "://" + Request.Host;
                  string redirectUri = requestPrefix + Request.PathBase + Options.CallbackPath;
      
                  // Build up the body for the token request
                  var body = new List<KeyValuePair<string, string>>();
                  body.Add(new KeyValuePair<string, string>("grant_type", "authorization_code"));
                  body.Add(new KeyValuePair<string, string>("code", code));
                  body.Add(new KeyValuePair<string, string>("redirect_uri", redirectUri));
                  body.Add(new KeyValuePair<string, string>("client_id", Options.ClientId));
                  body.Add(new KeyValuePair<string, string>("client_secret", Options.ClientSecret));
      
                  // Request the token
                  HttpResponseMessage tokenResponse =
                  await _httpClient.PostAsync(TokenEndpoint, new FormUrlEncodedContent(body));
                  tokenResponse.EnsureSuccessStatusCode();
                  string text = await tokenResponse.Content.ReadAsStringAsync();
      
                  // Deserializes the token response
                  JObject response = JObject.Parse(text);
                  string accessToken = response.Value<string>("access_token");
                  string expires = response.Value<string>("expires_in");
                  string refreshToken = response.Value<string>("refresh_token");
      
                  if (string.IsNullOrWhiteSpace(accessToken))
                  {
                      _logger.WriteWarning("Access token was not found");
                      return new AuthenticationTicket(null, properties);
                  }
      
                  // Get the Google user
                  HttpResponseMessage graphResponse = await _httpClient.GetAsync(
                      UserInfoEndpoint + Uri.EscapeDataString(accessToken), Request.CallCancelled);
                  graphResponse.EnsureSuccessStatusCode();
      
                  // i will show content of this var later
                  text = await graphResponse.Content.ReadAsStringAsync();
                  JObject user = JObject.Parse(text);
      
      
                  //because of permanent exception in GoogleOAuth2AuthenticatedContext constructor i prepare user data with my extension
                  JObject correctUser = OAuth2Helper.PrepareGoogleUserInfo(user);
      
                  // i've replaced this with selfprepared user2
                  //var context = new GoogleOAuth2AuthenticatedContext(Context, user, accessToken, refreshToken, expires);
                  var context = new GoogleOAuth2AuthenticatedContext(Context, correctUser, accessToken, refreshToken, expires);
                  context.Identity = new ClaimsIdentity(
                      Options.AuthenticationType,
                      ClaimsIdentity.DefaultNameClaimType,
                      ClaimsIdentity.DefaultRoleClaimType);
                  if (!string.IsNullOrEmpty(context.Id))
                  {
                      context.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, context.Id,
                      ClaimValueTypes.String, Options.AuthenticationType));
                  }
                  if (!string.IsNullOrEmpty(context.GivenName))
                  {
                      context.Identity.AddClaim(new Claim(ClaimTypes.GivenName, context.GivenName,
                      ClaimValueTypes.String, Options.AuthenticationType));
                  }
                  if (!string.IsNullOrEmpty(context.FamilyName))
                  {
                      context.Identity.AddClaim(new Claim(ClaimTypes.Surname, context.FamilyName,
                      ClaimValueTypes.String, Options.AuthenticationType));
                  }
                  if (!string.IsNullOrEmpty(context.Name))
                  {
                      context.Identity.AddClaim(new Claim(ClaimTypes.Name, context.Name, ClaimValueTypes.String,
                      Options.AuthenticationType));
                  }
                  if (!string.IsNullOrEmpty(context.Email))
                  {
                      context.Identity.AddClaim(new Claim(ClaimTypes.Email, context.Email, ClaimValueTypes.String,
                      Options.AuthenticationType));
                  }
      
                  if (!string.IsNullOrEmpty(context.Profile))
                  {
                      context.Identity.AddClaim(new Claim("urn:google:profile", context.Profile, ClaimValueTypes.String,
                      Options.AuthenticationType));
                  }
                  context.Properties = properties;
      
                  await Options.Provider.Authenticated(context);
      
                  return new AuthenticationTicket(context.Identity, context.Properties);
              }
              catch (Exception ex)
              {
                  _logger.WriteError("Authentication failed", ex);
                  return new AuthenticationTicket(null, properties);
              }
          }
      
          protected override Task ApplyResponseChallengeAsync()
          {
      
              // finaly! here it is. i just want to put this two lines here. thats all
              Options.ClientId = OAuth2Helper.GetProviderAppId("google");
              Options.ClientSecret = OAuth2Helper.GetProviderAppSecret("google");
      
              /* default code ot the method */
          }
      
          // no changes
          public override async Task<bool> InvokeAsync()
          {
          /* default code here */
          }
      
          // no changes
          private async Task<bool> InvokeReplyPathAsync()
          {
          /* default code here */
          }
      
          //  no changes
          private static void AddQueryString(IDictionary<string, string> queryStrings, AuthenticationProperties properties,
          string name, string defaultValue = null)
          {
          /* default code here */
          }   
      }
      

    After all i get some surprises.

    1. after myhost/signin-google i get myhost/Account/ExternalLoginCallback?error=access_denied and 302 redirect back to login page with no success.
      that is because of few Exception in internal methods of GoogleOAuth2AuthenticatedContext constructor.

      GivenName = TryGetValue(user, "name", "givenName");
      FamilyName = TryGetValue(user, "name", "familyName");
      

    and

        Email = TryGetFirstValue(user, "emails", "value");
    

    and here is the google response which we translate to JObject user

            {
            "sub": "XXXXXXXXXXXXXXXXXX",
            "name": "John Smith",
            "given_name": "John",
            "family_name": "Smith",
            "profile": "https://plus.google.com/XXXXXXXXXXXXXXXXXX",
            "picture": "https://lh5.googleusercontent.com/url-to-the-picture/photo.jpg",
            "email": "usermail@domain.com",
            "email_verified": true,
            "gender": "male",
            "locale": "ru",
            "hd": "google application website"
            }
    

    name is string and TryGetValue(user, "name", "givenName") will fail as TryGetValue(user, "name", "familyName")
    emails is missed

    thats why i used helper wich translate user to correct correctUser

    1. correctUser is ok but i still have no success. why? after myhost/signin-google i get myhost/Account/ExternalLoginCallback and 302 redirect back to login page with no success.

    id in google response is actualy sub so
    • Id property of AuthenticatedContext is not filled
    ClaimTypes.NameIdentifier never created
    • AccountController.ExternalLoginCallback(string returnUrl) will always redirect us because of loginInfo is null

    GetExternalLoginInfo takes AuthenticateResult wich should not be null and it checks result.Identity for ClaimTypes.NameIdentifier existence

    renaming sub into id do the work. now everything is ok.

    it seems that microsoft implementation of katana differs from katana source because if i use default everything is work without any magic.

    if you can correct me, if you know more easiest way to make owin work with AuthenticationOptions determined at runtime based on host name, please tell me

    解决方案

    I've recently battled with trying to get multi-tennancy working with the same OAuth provider but with different accounts. I know you wanted to update the options dynamically at runtime but you might not need to do that, hopefully this helps...

    I think the reason that you don't have this working, even with overriding all of those classes is because each configured google OAuth account needs to have a unique CallbackPath. This is what determines which registered provider and options will execute on the callback.

    Instead of trying to do this dynamically, you can declare each OAuth provider at startup and ensure they have unique AuthenticationType and unique CallbackPath, for example:

    //Provider #1
    app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions
    {
        AuthenticationType = "Google-Site.Com",
        ClientId = "abcdef...",
        ClientSecret = "zyxwv....",
        CallbackPath = new PathString("/sitecom-signin-google")
    });
    
    //Provider #2
    app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions
    {
        AuthenticationType = "Google-AnotherSite.com",
        ClientId = "abcdef...",
        ClientSecret = "zyxwv....",
        CallbackPath = new PathString("/anothersitecom-signin-google")
    });
    

    Then where you are calling IOwinContext.Authentication.Challenge you make sure to pass it your correctly named AuthenticationType for the current tenant you want to authenticate. Example: HttpContext.GetOwinContext().Authentication.Challenge(properties, "Google-AnotherSite.com");

    The next step is to update your callback path in Google's Developers Console to match your custom callback paths. By default it is "signin-google" but each of these needs to be unique among your declared providers so that the provider knows it needs to handle the specific callback on that path.

    I actually just blogged about all of this here in more detail: http://shazwazza.com/post/configuring-aspnet-identity-oauth-login-providers-for-multi-tenancy/

    这篇关于OWIN AuthenticationOptions在mvc5应用程序在运行时更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆