Web Api 2 用户认证模板解析-----外部用户认证模式

一般的社交提供商不提供一个Web Service进行身份验证(有很好的理由),而提供一个身份验证的界面,其中包含了某种协议如OpenID(连接)或使用OAuth2认证。这意味着客户端应用必须使用一个浏览器来展现这些UI部件---典型的登录页面和一些类型的许可界面。

这通常通过导航到一个众所周知的URL (网页API的模板使用OAuth2隐流程方法),并等待,直到在其他一些知名的URL会发生回调。在回调的访问令牌(加上一些元数据)通过散列片段连接。什么是之间发生的事情是总长达授权服务器,其与外部登录供应商的协调。客户端不需要知道该怎么做,这是很好的。

个人帐户帐户控制功能,查询已注册的中间件,并告诉和客户端的外部供应商提供哪些URL它必须使用来获得流量去的端点。这个信息可以被用来塑造客户端应用程序的用户界面:

private async Task GetExternalLoginsAsync()
{
var client = new HttpClient { BaseAddress = new Uri(_baseAddress) };
var response = await client.GetAsync(
“api/account/externalLogins?returnUrl=/&generateState=true”);
response.EnsureSuccessStatusCode(); _externalLogins = await response.Content.ReadAsAsync<
List<ExternalLoginViewModel>>();
}

外部登录模型如:

public class ExternalLoginViewModel
{
public string Name { get; set; }
public string Url { get; set; }
public string State { get; set; }
}

Url is the start URL (the callback URL is hardcoded to the base address in the template) and State is a random number that gets round tripped and should be checked on the callback (see the OAuth2 spec for details). The Url looks like this (sample):

/api/Account/ExternalLogin?
  provider=Google&
  response_type=token&
  client_id=self&
  redirect_uri=https://localhost/&
  state=as..aaa

The /api/account/externalLogin endpoint maps to the GetExternalLogin action on the account controller which has quite an interesting combination of attributes:

[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalCookie)]
[AllowAnonymous]
[Route("ExternalLogin", Name = "ExternalLogin")]

This basically means: don’t care about bearer tokens but allow anonymous access or an external cookie (see part 1).

In addition this endpoint also maps to the authorize endpoint of the OAuth2 authorization server middleware (check startup) which is kind of a black belt trick. First the middleware processes the request and then passes it on to whatever framework handles the URL – in this case Web API.

The exact course of events is quite involved. Let’s start with the moment when the web view hits the URL for the first time (check out the source code in parallel):

  1. Access /api/account/externalLogin?provider=Google…
    1. the user is not authenticated
    2. signal the middleware that is responsible for the requested external provider to do the protocol handshake (in this case Google)
  2. The user signs in at Google and does the consent
    1. Google calls back to the Web API using the /signin-google URL
  3. The Google middleware does its back channel communication and sets an external cookie containing some of the claims that came back from Google
    1. the middleware then redirects back to the /api/account/externalLogin endpoint
  4. This time the user is authenticated via the external cookie
    1. The account controller now checks if the login provider / user id combination is already registered in the local database
    2. this is not the case, so the account controller passes control back to the authorization server middleware instructing it to issue a token that contains the claims from Google (external bearer – the claims issuer is Google – again – check part one for the subtle meaning of that).
  5. The callback URL is invoked, and the client application retrieves the access token

At this point the client app has an access token, but it won’t get accepted by default since the claims are not issued by LOCAL AUTHORITY but Google instead. At this point you could open up endpoints by adding:

[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]

One endpoint that is configured exactly like that is the /api/account/registerExternal. This endpoints accepts the token you get from the previous step and allows it to associate with a new local account (without a password):

private void RegisterExternal_Click(object sender, RoutedEventArgs e)
{
var client = new HttpClient { BaseAddress = new Uri(_baseAddress) };
client.SetBearerToken(_externalResponse.AccessToken); var data = new Dictionary<string, string>
{
{ “UserName”, “dominick” }
}; var response = client.PostAsync(
“api/account/registerExternal”,
new FormUrlEncodedContent(data)).Result;
response.EnsureSuccessStatusCode();
}

RegisterExternal uses the ASP.Identity API to create a new user account and associates that with the external provider and user id from the token.

Now the last step is to go back to /api/account/externalLogin to get a new token that now represents a local account (or technically speaking claims from LOCAL AUTHORITY). The flow is like this:

  1. Invoke externalLogin with the same URL as before
    1. the user is still authenticated using the same external cookie
    2. the login provider / user id pair is now registered
  2. The controller clears the external cookie
    1. and creates a new authentication ticket
    2. this ticket translates to a new token with an issuer of LOCAL AUTHORITY – aka local account
  3. The callback URL transmits the token back to the client

Mission accomplished – the client used an external login provider to authenticate the user and linked that login to an account in its backend. Quite a stunt.

上一篇:sql 查询某个条件多条数据中最新的一条数据或最老的一条数据


下一篇:python中str相关函数