没有 cookie 或本地凭据的外部 Owin 身份验证
External Owin Authentication without cookies or Local credentials
我正在使用 angular 和 webapi 开发跨平台网络应用程序。问题是当 angular 应用程序在 cordova 容器中运行时。为了与设备上的其他应用程序配合使用,我需要使用 SSO 插件。这个插件是导致我出现问题的原因,因为它做了一些事情。它拦截所有 http 请求并向 header 添加一个承载令牌,该令牌由第 3 方令牌提供商生成,因此我无法对其进行解码,并覆盖我在 [=25] 中设置的任何承载令牌=] 似乎也阻止了 cookies..
因此,当您无法发送自己的本地凭据时,这会有点棘手。
所以我从 https://coding.abel.nu/2014/06/writing-an-owin-authentication-middleware/ and http://katanaproject.codeplex.com/SourceControl/latest#src/Microsoft.Owin.Security.OAuth/OAuthBearerAuthenticationHandler.cs
开始
所以我想我应该编写自己的中间件来处理这个问题;我想既然标准的 oauth 中间件可以在没有 cookie 的情况下工作,我不应该花太多时间让我稍微不同的不记名令牌中间件来做它......但事实并非如此......编写我自己的中间件......所以我我能够获得 header,通过外部令牌提供商验证,但我无法实际登录。
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
{
try
{
// Find token in default location
string requestToken = null;
string authorization = Request.Headers.Get("Authorization");
if (!string.IsNullOrEmpty(authorization))
{
if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
{
requestToken = authorization.Substring("Bearer ".Length).Trim();
}
}
.... Take the Request token call other Server, verify token...
还有
public override async Task<bool> InvokeAsync()
{
var ticket = await this.AuthenticateAsync();
if(ticket != null)
{
this.Context.Authentication.SignIn(new AuthenticationProperties(), grantIdentity);
return false;
}
}
所以最后登录并没有导致错误或任何东西,但实际上并没有登录。一旦我到达具有 [Authorize] 属性的控制器操作,我就会收到 401。我没有启用任何外部 cookie。我很可能走错了路,或者我做得太难了。
你做得太辛苦了
与其创建自己的承载身份验证中间件,不如更改默认值 OAuthBearerAuthenticationProvider
。
这是在查询字符串中发送令牌的示例。
//in Startup class
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
Provider = new QueryStringOAuthBearerProvider(),
//your settings
});
//implementation
public class QueryStringOAuthBearerProvider : OAuthBearerAuthenticationProvider
{
private const string AccessTokenQueryKey = "access_token";
public override Task RequestToken(OAuthRequestTokenContext context)
{
//check if token found in the default location - "Authorization: Bearer <token>" header
if (string.IsNullOrEmpty(context.Token))
{
var token = context.Request.Query.Get(AccessTokenQueryKey);
if (!string.IsNullOrEmpty(token))
{
context.Token = token;
}
}
return Task.FromResult<object>(null);
}
}
所以……我想早点回答这个问题,但我能够在不覆盖授权属性的情况下解决这个问题。我最终查看了 OWIN 安全代码的源代码。诀窍是,您确实需要 2 个 OWIN 中间件组件。一个是我称之为(我从 owin 源代码中窃取的)服务器中间件。服务器中间件响应挑战 and/or 如果你觉得疯狂为你生成本地凭据。该中间件也是一个 PASSIVE 中间件组件。除非有人问,否则我不会开始生成本地凭据,因为这有点离题,但如果有人认为这会有帮助,我可以更新。
public class LowCalorieAuthenticationServerHandler : AuthenticationHandler<LowCalorieAuthenticationServerOptions>
{
//Important this needs to be overriden, but just calls the base.
protected override Task<AuthenticationTicket> AuthenticateCoreAsync()
{
return Task.FromResult<AuthenticationTicket>(null);
}
/// <summary>The apply response challenge async.</summary>
/// <returns>The <see cref="Task"/>.</returns>
protected override async Task ApplyResponseChallengeAsync()
{
if (this.Response.StatusCode != 401)
{
Task.FromResult<object>(null);
return;
}
var challenge = this.Helper.LookupChallenge(
this.Options.AuthenticationType,
this.Options.AuthenticationMode);
if (challenge != null)
{
//OK in here you call the rediret to the 3rd party
//return a redirect to some endpoint
}
Task.FromResult<object>(null);
return;
}
}
无论如何请注意如何覆盖 AuthenticateCoreAsync() returns
return Task.FromResult(空);
这是因为我们不希望这个中间件修改请求。 ApplyResponseChallengeAsync 将等待挑战并将您重定向到第 3 方登录。如果您想创建某种本地令牌,您可以重写 InvokeAsync 方法
您需要的第二个中间件是 token/external 凭证验证器。然后,这将以某种方式对用户进行身份验证。对于内置于 OWIN 安全性中的本地不记名令牌,它会简单地反序列化令牌,如果可以,并且令牌没有过期,它会对用户进行身份验证。因此,如果您想使用第 3 部分 sso 验证令牌,例如 google 或任何东西,您可以在此处插入逻辑。在我的例子中,我不仅想调用第 3 方提供商来获取用户信息,还想检查他们的令牌是否仍然对单点注销有效,并防止多次会话。
public class LowCalorieAuthenticationHandler : AuthenticationHandler<LowCalorieAuthenticationOptions>
{
//Going to give you the user for the request.. You Need to do 3 things here
//1. Get the user claim from teh request somehow, either froma header, request string, or cookie what ever you want
//2. validate the user with whatever user store or 3rd party SSO you want
//3. Generate a AuthenticationTicket to send to on to the request, you can use that to see if the user is valid in any Identity collection you want.
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
{
//Good to throw in a point of override here.. but to keep it simple-ish
string requestToken = null;
string authorization = Request.Headers.Get("Authorization");
//TOTAL FAKEOUT.. I am going to add a bearer token just so the simple sample works, but your client would have to provide this
authorization = "Bearer 1234567869";
//STEP 1
if (!string.IsNullOrEmpty(authorization) && authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
{
requestToken = authorization.Substring("Bearer ".Length).Trim();
return await FakeExternalBearer(requestToken);
}
return null;
}
private async Task<AuthenticationTicket> FakeExternalBearer(string token)
{
var authenticationType = Options.AuthenticationType;
//pretend to call extenal Resource server to get user //STEP 2
//CallExternal(token)
//Create the AuthTicket from the return.. I will fake it out
var identity = new ClaimsIdentity(
authenticationType,
ClaimsIdentity.DefaultNameClaimType,
ClaimsIdentity.DefaultRoleClaimType);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier,"user1", null, authenticationType));
identity.AddClaim(new Claim(ClaimTypes.Name, "Jon",null, authenticationType));
var properties = new AuthenticationProperties();
properties.ExpiresUtc = DateTime.UtcNow.AddMinutes(1);
properties.IssuedUtc = DateTime.UtcNow;
var ticket = new AuthenticationTicket(identity, properties);
return ticket;
}
}
好的,我们在这里覆盖了 AuthenticateCoreAsync,但我们现在实际上做了一些事情。这是你的用户身份验证。这是中间件的 ACTIVE 部分。请注意,它需要 return 一个有效的 AuthenticationTicket。这将 运行 处理每个请求,因此请注意您的呼叫内容和频率。
所以我在这里有一个非常简单的例子https://github.com/jzoss/LowCalorieOwin 如果有人有兴趣了解更多细节,请询问。我可以添加更多。我确实把它弄得太难了,因为现在我明白了,这很容易,但是真的没有很好的例子来说明如何做到这一点。
我正在使用 angular 和 webapi 开发跨平台网络应用程序。问题是当 angular 应用程序在 cordova 容器中运行时。为了与设备上的其他应用程序配合使用,我需要使用 SSO 插件。这个插件是导致我出现问题的原因,因为它做了一些事情。它拦截所有 http 请求并向 header 添加一个承载令牌,该令牌由第 3 方令牌提供商生成,因此我无法对其进行解码,并覆盖我在 [=25] 中设置的任何承载令牌=] 似乎也阻止了 cookies..
因此,当您无法发送自己的本地凭据时,这会有点棘手。
所以我从 https://coding.abel.nu/2014/06/writing-an-owin-authentication-middleware/ and http://katanaproject.codeplex.com/SourceControl/latest#src/Microsoft.Owin.Security.OAuth/OAuthBearerAuthenticationHandler.cs
开始所以我想我应该编写自己的中间件来处理这个问题;我想既然标准的 oauth 中间件可以在没有 cookie 的情况下工作,我不应该花太多时间让我稍微不同的不记名令牌中间件来做它......但事实并非如此......编写我自己的中间件......所以我我能够获得 header,通过外部令牌提供商验证,但我无法实际登录。
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
{
try
{
// Find token in default location
string requestToken = null;
string authorization = Request.Headers.Get("Authorization");
if (!string.IsNullOrEmpty(authorization))
{
if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
{
requestToken = authorization.Substring("Bearer ".Length).Trim();
}
}
.... Take the Request token call other Server, verify token...
还有
public override async Task<bool> InvokeAsync()
{
var ticket = await this.AuthenticateAsync();
if(ticket != null)
{
this.Context.Authentication.SignIn(new AuthenticationProperties(), grantIdentity);
return false;
}
}
所以最后登录并没有导致错误或任何东西,但实际上并没有登录。一旦我到达具有 [Authorize] 属性的控制器操作,我就会收到 401。我没有启用任何外部 cookie。我很可能走错了路,或者我做得太难了。
你做得太辛苦了
与其创建自己的承载身份验证中间件,不如更改默认值 OAuthBearerAuthenticationProvider
。
这是在查询字符串中发送令牌的示例。
//in Startup class
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
Provider = new QueryStringOAuthBearerProvider(),
//your settings
});
//implementation
public class QueryStringOAuthBearerProvider : OAuthBearerAuthenticationProvider
{
private const string AccessTokenQueryKey = "access_token";
public override Task RequestToken(OAuthRequestTokenContext context)
{
//check if token found in the default location - "Authorization: Bearer <token>" header
if (string.IsNullOrEmpty(context.Token))
{
var token = context.Request.Query.Get(AccessTokenQueryKey);
if (!string.IsNullOrEmpty(token))
{
context.Token = token;
}
}
return Task.FromResult<object>(null);
}
}
所以……我想早点回答这个问题,但我能够在不覆盖授权属性的情况下解决这个问题。我最终查看了 OWIN 安全代码的源代码。诀窍是,您确实需要 2 个 OWIN 中间件组件。一个是我称之为(我从 owin 源代码中窃取的)服务器中间件。服务器中间件响应挑战 and/or 如果你觉得疯狂为你生成本地凭据。该中间件也是一个 PASSIVE 中间件组件。除非有人问,否则我不会开始生成本地凭据,因为这有点离题,但如果有人认为这会有帮助,我可以更新。
public class LowCalorieAuthenticationServerHandler : AuthenticationHandler<LowCalorieAuthenticationServerOptions>
{
//Important this needs to be overriden, but just calls the base.
protected override Task<AuthenticationTicket> AuthenticateCoreAsync()
{
return Task.FromResult<AuthenticationTicket>(null);
}
/// <summary>The apply response challenge async.</summary>
/// <returns>The <see cref="Task"/>.</returns>
protected override async Task ApplyResponseChallengeAsync()
{
if (this.Response.StatusCode != 401)
{
Task.FromResult<object>(null);
return;
}
var challenge = this.Helper.LookupChallenge(
this.Options.AuthenticationType,
this.Options.AuthenticationMode);
if (challenge != null)
{
//OK in here you call the rediret to the 3rd party
//return a redirect to some endpoint
}
Task.FromResult<object>(null);
return;
}
}
无论如何请注意如何覆盖 AuthenticateCoreAsync() returns return Task.FromResult(空); 这是因为我们不希望这个中间件修改请求。 ApplyResponseChallengeAsync 将等待挑战并将您重定向到第 3 方登录。如果您想创建某种本地令牌,您可以重写 InvokeAsync 方法
您需要的第二个中间件是 token/external 凭证验证器。然后,这将以某种方式对用户进行身份验证。对于内置于 OWIN 安全性中的本地不记名令牌,它会简单地反序列化令牌,如果可以,并且令牌没有过期,它会对用户进行身份验证。因此,如果您想使用第 3 部分 sso 验证令牌,例如 google 或任何东西,您可以在此处插入逻辑。在我的例子中,我不仅想调用第 3 方提供商来获取用户信息,还想检查他们的令牌是否仍然对单点注销有效,并防止多次会话。
public class LowCalorieAuthenticationHandler : AuthenticationHandler<LowCalorieAuthenticationOptions>
{
//Going to give you the user for the request.. You Need to do 3 things here
//1. Get the user claim from teh request somehow, either froma header, request string, or cookie what ever you want
//2. validate the user with whatever user store or 3rd party SSO you want
//3. Generate a AuthenticationTicket to send to on to the request, you can use that to see if the user is valid in any Identity collection you want.
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
{
//Good to throw in a point of override here.. but to keep it simple-ish
string requestToken = null;
string authorization = Request.Headers.Get("Authorization");
//TOTAL FAKEOUT.. I am going to add a bearer token just so the simple sample works, but your client would have to provide this
authorization = "Bearer 1234567869";
//STEP 1
if (!string.IsNullOrEmpty(authorization) && authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
{
requestToken = authorization.Substring("Bearer ".Length).Trim();
return await FakeExternalBearer(requestToken);
}
return null;
}
private async Task<AuthenticationTicket> FakeExternalBearer(string token)
{
var authenticationType = Options.AuthenticationType;
//pretend to call extenal Resource server to get user //STEP 2
//CallExternal(token)
//Create the AuthTicket from the return.. I will fake it out
var identity = new ClaimsIdentity(
authenticationType,
ClaimsIdentity.DefaultNameClaimType,
ClaimsIdentity.DefaultRoleClaimType);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier,"user1", null, authenticationType));
identity.AddClaim(new Claim(ClaimTypes.Name, "Jon",null, authenticationType));
var properties = new AuthenticationProperties();
properties.ExpiresUtc = DateTime.UtcNow.AddMinutes(1);
properties.IssuedUtc = DateTime.UtcNow;
var ticket = new AuthenticationTicket(identity, properties);
return ticket;
}
}
好的,我们在这里覆盖了 AuthenticateCoreAsync,但我们现在实际上做了一些事情。这是你的用户身份验证。这是中间件的 ACTIVE 部分。请注意,它需要 return 一个有效的 AuthenticationTicket。这将 运行 处理每个请求,因此请注意您的呼叫内容和频率。 所以我在这里有一个非常简单的例子https://github.com/jzoss/LowCalorieOwin 如果有人有兴趣了解更多细节,请询问。我可以添加更多。我确实把它弄得太难了,因为现在我明白了,这很容易,但是真的没有很好的例子来说明如何做到这一点。