- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试为我的 slackbot 创建一个非常简单的页面,以便用户可以登录和注册。但是,即使使用他们生成的“使用 Slack 登录”按钮,我也会收到错误“oauth 状态丢失或无效。”。 “添加到 Slack”也会发生同样的错误。
我的代码基于 https://dotnetthoughts.net/slack-authentication-with-aspnet-core/ 。尽管它已经过时了,但这是我在网上找到的唯一示例。我尝试找出需要更改哪些内容才能使其与 dotnetcore 3 和 Slack 2.0 配合使用,但我已无计可施。
在我的服务中,在调用AddMvc等之前我有以下内容
services.AddAuthentication(options =>
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Cookie.Name = "MyAuthCookieName";
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.MaxAge = TimeSpan.FromDays(7);
options.ExpireTimeSpan = TimeSpan.FromDays(7);
options.LoginPath = $"/login";
options.LogoutPath = $"/logout";
options.AccessDeniedPath = $"/AccessDenied";
options.SlidingExpiration = true;
options.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
})
//.AddSlack(options =>
//{
// options.ClientId = Configuration["Slack:ClientId"];
// options.ClientSecret = Configuration["Slack:ClientSecret"];
//});
.AddOAuth("Slack", options =>
{
options.ClientId = Configuration["Slack:ClientId"];
options.ClientSecret = Configuration["Slack:ClientSecret"];
options.CallbackPath = new PathString("/signin-slack");
options.AuthorizationEndpoint = $"https://slack.com/oauth/authorize";
options.TokenEndpoint = "https://slack.com/api/oauth.access";
options.UserInformationEndpoint = "https://slack.com/api/users.identity?token=";
options.Scope.Add("identity.basic");
options.Events = new OAuthEvents()
{
OnCreatingTicket = async context =>
{
var request = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint + context.AccessToken);
var response = await context.Backchannel.SendAsync(request, context.HttpContext.RequestAborted);
response.EnsureSuccessStatusCode();
var userObject = JObject.Parse(await response.Content.ReadAsStringAsync());
var user = userObject.SelectToken("user");
var userId = user.Value<string>("id");
if (!string.IsNullOrEmpty(userId))
{
context.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userId, ClaimValueTypes.String, context.Options.ClaimsIssuer));
}
var fullName = user.Value<string>("name");
if (!string.IsNullOrEmpty(fullName))
{
context.Identity.AddClaim(new Claim(ClaimTypes.Name, fullName, ClaimValueTypes.String, context.Options.ClaimsIssuer));
}
}
};
});
我的配置方法看起来像
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.Map("/login", builder =>
{
builder.Run(async context =>
{
await context.ChallengeAsync("Slack", properties: new AuthenticationProperties { RedirectUri = "/" });
});
});
app.Map("/logout", builder =>
{
builder.Run(async context =>
{
await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
context.Response.Redirect("/");
});
});
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapRazorPages();
});
除了“oauth 状态无效时丢失”之外,如果在我的应用程序中直接转到/login,我不会收到错误,但我似乎没有以 User 身份登录。 Identity.IsAuthenticated
为 false。
我真的很茫然,需要一些非常感谢的帮助!
谢谢!
大规模更新
我登录到 Slack 后可以正常工作,但无法使用“添加到 Slack”按钮。
这是我的新服务:
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
options.LoginPath = "/login";
options.LogoutPath = "/logout";
})
.AddSlack(options =>
{
options.ClientId = Configuration["Slack:ClientId"];
options.ClientSecret = Configuration["Slack:ClientSecret"];
options.CallbackPath = $"{SlackAuthenticationDefaults.CallbackPath}?state={Guid.NewGuid():N}";
options.ReturnUrlParameter = new PathString("/");
options.Events = new OAuthEvents()
{
OnCreatingTicket = async context =>
{
var request = new HttpRequestMessage(HttpMethod.Get, $"{context.Options.UserInformationEndpoint}?token={context.AccessToken}");
var response = await context.Backchannel.SendAsync(request, context.HttpContext.RequestAborted);
response.EnsureSuccessStatusCode();
var userObject = JObject.Parse(await response.Content.ReadAsStringAsync());
var user = userObject.SelectToken("user");
var userId = user.Value<string>("id");
if (!string.IsNullOrEmpty(userId))
{
context.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userId, ClaimValueTypes.String, context.Options.ClaimsIssuer));
}
var fullName = user.Value<string>("name");
if (!string.IsNullOrEmpty(fullName))
{
context.Identity.AddClaim(new Claim(ClaimTypes.Name, fullName, ClaimValueTypes.String, context.Options.ClaimsIssuer));
}
}
};
});
根据@timur,我抓取了我的 app.Map 并使用了身份验证 Controller :
public class AuthenticationController : Controller
{
[HttpGet("~/login")]
public async Task<IActionResult> SignIn()
{
return Challenge(new AuthenticationProperties { RedirectUri = "/" }, "Slack");
}
[HttpGet("~/signin-slack")]
public IActionResult SignInSlack()
{
return RedirectToPage("/Index");
}
[HttpGet("~/logout"), HttpPost("~/logout")]
public IActionResult SignOut()
{
return SignOut(new AuthenticationProperties { RedirectUri = "/" },
CookieAuthenticationDefaults.AuthenticationScheme);
}
}
“添加到 Slack”按钮按原样由 Slack 提供。
<a href="https://slack.com/oauth/authorize?scope=incoming-webhook,commands,bot&client_id=#############"><img alt="Add to Slack" height="40" width="139" src="https://platform.slack-edge.com/img/add_to_slack.png" srcset="https://platform.slack-edge.com/img/add_to_slack.png 1x, https://platform.slack-edge.com/img/add_to_slack@2x.png 2x" /></a>
因此,当用户单击“登录”时,它会将他们登录并获取他们的姓名等。您会注意到,在我的身份验证 Controller 中,我添加了一个带有路径“~/signin-slack”的函数,这是因为我手动添加“Options.CallbackPath”以添加状态参数。如果删除“Options.CallbackPath”,我会收到一条错误,指出 oauth 状态丢失或无效。
所以,我不确定 Slack 方面缺少什么。他们让它听起来如此简单!
抱歉,帖子/更新很长。感谢您的帮助。
最佳答案
您提到的同一篇文章下面有一个链接,指向 AspNet.Security.OAuth.Providers源代码库。这似乎相当活跃,并且支持大量其他 oAuth 目标,包括 Slack。
我假设您已经创建并配置了您的 Slack 应用程序。 重定向 URL 部分在这里至关重要,因为无论您指定 http 还是 https 回调(我的示例仅在使用 https 时才有效),这都很重要。
综上所述,我相信实现它的一般方法是
Install-Package AspNet.Security.OAuth.Slack -Version 3.0.0
并像这样编辑您的Startup.cs
:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(options => { /* your options verbatim */ })
.AddSlack(options =>
{
options.ClientId = "xxx";
options.ClientSecret = "xxx";
});
}
我看到您选择直接在 Startup 类中映射登录/注销路由,这实际上可能是问题所在 - 对 .Map()
的调用会分支请求管道,因此您不会点击您之前设置的相同中间件链),所以我使用了一个单独的 Controller (根据 sample app ):
public class AuthenticationController : Controller
{
[HttpGet("~/signin")]
public async Task<IActionResult> SignIn()
{
// Instruct the middleware corresponding to the requested external identity
// provider to redirect the user agent to its own authorization endpoint.
// Note: the authenticationScheme parameter must match the value configured in Startup.cs
return Challenge(new AuthenticationProperties { RedirectUri = "/" }, "Slack");
}
[HttpGet("~/signout"), HttpPost("~/signout")]
public IActionResult SignOut()
{
// Instruct the cookies middleware to delete the local cookie created
// when the user agent is redirected from the external identity provider
// after a successful authentication flow (e.g Google or Facebook).
return SignOut(new AuthenticationProperties { RedirectUri = "/" },
CookieAuthenticationDefaults.AuthenticationScheme);
}
}
但是看着你的代码片段,我怀疑你已经安装了这个 nuget 包并尝试使用它。这让我推荐一些需要检查的东西:
identity.basic
范围**项目属性** -> **调试**选项卡 -> **启用 SSL** 复选框
(如果 IIS Express 托管,否则您可能需要做 bit of extra work )UPD:经过反复讨论,我能够更好地了解您的问题。我确实相信您所观察到的内容与使用 slack 登录是分开的,而是与他们的应用程序安装流程有关。正如您已经指出的,“添加到 slack”流程和用户登录之间的区别是 - state
参数不是源 URL 的一部分,因此不会跨请求返回给您。这对于 oAuth 处理程序来说意义重大,因为它依赖 state
来验证请求完整性,如果 state 为空,则会失败。有一个discussion on github但我相信的结果是 - 你将不得不自己跳过验证部分。因此,我继承了 nuget 包附带的 SlackAuthenticationHandler
并删除了导致问题的代码:
public class SlackNoStateAuthenticationHandler : SlackAuthenticationHandler {
public SlackNoStateAuthenticationHandler([NotNull] IOptionsMonitor<SlackAuthenticationOptions> options,
[NotNull] ILoggerFactory logger,
[NotNull] UrlEncoder encoder,
[NotNull] ISystemClock clock) : base(options, logger, encoder, clock) { }
public void GenerateCorrelationIdPublic(AuthenticationProperties properties)
{
GenerateCorrelationId(properties);
}
protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync()
{
var query = Request.Query;
var state = query["state"];
var properties = Options.StateDataFormat.Unprotect(state);
var error = query["error"];
if (!StringValues.IsNullOrEmpty(error))
{
// Note: access_denied errors are special protocol errors indicating the user didn't
// approve the authorization demand requested by the remote authorization server.
// Since it's a frequent scenario (that is not caused by incorrect configuration),
// denied errors are handled differently using HandleAccessDeniedErrorAsync().
// Visit https://tools.ietf.org/html/rfc6749#section-4.1.2.1 for more information.
if (StringValues.Equals(error, "access_denied"))
{
return await HandleAccessDeniedErrorAsync(properties);
}
var failureMessage = new StringBuilder();
failureMessage.Append(error);
var errorDescription = query["error_description"];
if (!StringValues.IsNullOrEmpty(errorDescription))
{
failureMessage.Append(";Description=").Append(errorDescription);
}
var errorUri = query["error_uri"];
if (!StringValues.IsNullOrEmpty(errorUri))
{
failureMessage.Append(";Uri=").Append(errorUri);
}
return HandleRequestResult.Fail(failureMessage.ToString(), properties);
}
var code = query["code"];
if (StringValues.IsNullOrEmpty(code))
{
return HandleRequestResult.Fail("Code was not found.", properties);
}
var tokens = await ExchangeCodeAsync(new OAuthCodeExchangeContext(properties, code, BuildRedirectUri(Options.CallbackPath)));
if (tokens.Error != null)
{
return HandleRequestResult.Fail(tokens.Error, properties);
}
if (string.IsNullOrEmpty(tokens.AccessToken))
{
return HandleRequestResult.Fail("Failed to retrieve access token.", properties);
}
var identity = new ClaimsIdentity(ClaimsIssuer);
if (Options.SaveTokens)
{
var authTokens = new List<AuthenticationToken>();
authTokens.Add(new AuthenticationToken { Name = "access_token", Value = tokens.AccessToken });
if (!string.IsNullOrEmpty(tokens.RefreshToken))
{
authTokens.Add(new AuthenticationToken { Name = "refresh_token", Value = tokens.RefreshToken });
}
if (!string.IsNullOrEmpty(tokens.TokenType))
{
authTokens.Add(new AuthenticationToken { Name = "token_type", Value = tokens.TokenType });
}
if (!string.IsNullOrEmpty(tokens.ExpiresIn))
{
int value;
if (int.TryParse(tokens.ExpiresIn, NumberStyles.Integer, CultureInfo.InvariantCulture, out value))
{
// https://www.w3.org/TR/xmlschema-2/#dateTime
// https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx
var expiresAt = Clock.UtcNow + TimeSpan.FromSeconds(value);
authTokens.Add(new AuthenticationToken
{
Name = "expires_at",
Value = expiresAt.ToString("o", CultureInfo.InvariantCulture)
});
}
}
properties.StoreTokens(authTokens);
}
var ticket = await CreateTicketAsync(identity, properties, tokens);
if (ticket != null)
{
return HandleRequestResult.Success(ticket);
}
else
{
return HandleRequestResult.Fail("Failed to retrieve user information from remote server.", properties);
}
}
}
此代码的大部分内容都是 relevant source 的逐字副本。 ,因此您可以根据需要随时进行更多更改;
然后我们需要将合理的状态参数注入(inject)到您的 URL 中。假设您有一个 Controller 和一个 View :
public class HomeController : Controller
{
private readonly IAuthenticationHandlerProvider _handler;
public HomeController(IAuthenticationHandlerProvider handler)
{
_handler = handler;
}
public async Task<IActionResult> Index()
{
var handler = await _handler.GetHandlerAsync(HttpContext, "Slack") as SlackNoStateAuthenticationHandler; // we'd get the configured instance
var props = new AuthenticationProperties { RedirectUri = "/" }; // provide some sane defaults
handler.GenerateCorrelationIdPublic(props); // generate xsrf token and add it into the properties object
ViewBag.state = handler.Options.StateDataFormat.Protect(props); // and push it into your view.
return View();
}
}
.AddOAuth<SlackAuthenticationOptions, SlackNoStateAuthenticationHandler>(SlackAuthenticationDefaults.AuthenticationScheme, SlackAuthenticationDefaults.DisplayName, options =>
{
options.ClientId = "your_id";
options.ClientSecret = "your_secret";
});
<a href="https://slack.com/oauth/authorize?client_id=<your_id>&scope=identity.basic&state=@ViewBag.state"><img alt="Add to Slack" height="40" width="139" src="https://platform.slack-edge.com/img/add_to_slack.png" srcset="https://platform.slack-edge.com/img/add_to_slack.png 1x, https://platform.slack-edge.com/img/add_to_slack@2x.png 2x"></a>
这使我能够成功完成请求,尽管我不完全确定这样做是否会被视为最佳实践
关于c# - 添加到 dotnetcore 中的 Slack,没有 Identity Framework 错误 : The oauth state was missing or invalid,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59507381/
使用新版本的 VS 2013 RTM 和 asp.net mvc 5.0,我决定尝试一些东西... 不用说,发生了很多变化。例如,新的 ASP.NET Identity 取代了旧的 Membershi
请参阅下面的代码: var result = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model
我对 asp.net 核心标识中的三个包感到困惑。我不知道彼此之间有什么区别。还有哪些是我们应该使用的? 我在 GitHub 上找到了这个链接,但我没有找到。 Difference between M
Visual Studio-为AspNet Identity 生成一堆代码,即LoginController 和ManageController。在 ManageController 中有以下代码:
我是 SwiftUI 的新手,在连续显示警报时遇到问题。 .alert(item:content:) 的描述修饰符在它的定义中写了这个: /// Presents an alert. ///
我有一个 scalaz Disjunction,其类型与 Disjunction[String, String] 相同,我只想获取值,无论它是什么。因此,我使用了 myDisjunction.fold
我有一个 ASP.NET MVC 应用程序,我正在使用 ASP.NET Identity 2。我遇到了一个奇怪的问题。 ApplicationUser.GenerateUserIdentityAsyn
安全戳是根据用户的用户名和密码生成的随机值。 在一系列方法调用之后,我将安全标记的来源追溯到 SecurityStamp。 Microsoft.AspNet.Identity.EntityFramew
我知道 Scope_Identity()、Identity()、@@Identity 和 Ident_Current() 全部获取身份列的值,但我很想知道其中的区别。 我遇到的部分争议是,应用于上述这
我正在使用 ASP.NET 5 beta 8 和 Identity Server 3 以及 AspNet Identity 用户服务实现。默认情况下,AspNet Identity 提供名为 AspN
我想在identity 用户中上传头像,并在账户管理中更新。如果有任何关于 asp.net core 的好例子的帖子,请给我链接。 最佳答案 我自己用 FileForm 方法完成的。首先,您必须在用户
在 ASP.NET 5 中,假设我有以下 Controller : [Route("api/[controller]")] [Authorize(Roles = "Super")] public cl
集成外部提供商(即Google与Thinktecture Identity Server v3)时出现问题。出现以下错误:“客户端应用程序未知或未获得授权。” 是否有人对此错误有任何想法。 最佳答案
我有一个 ASP.NET MVC 5 项目( Razor 引擎),它具有带有个人用户帐户的 Identity 2.0。我正在使用 Visual Studio Professional 2013 我还没
我配置IdentityServer4使用 AspNet Identity (.net core 3.0) 以允许用户进行身份验证(登录名/密码)。 我的第三个应用程序是 .net core 3.0 中
我创建了一个全新的 Web 应用程序,比如“WebApplication1” - 身份验证设置为个人用户帐户的 WebForms。我不会在自动生成的代码模板中添加一行代码。我运行应用程序并注册用户“U
是否可以为“系统”ASP.NET Identity v1 错误消息提供本地化字符串,例如“名称 XYZ 已被占用”或“用户名 XYZ 无效,可以只包含字母或数字”? 最佳答案 对于 ASP.NET C
我对 Windows Identity Foundation (WIF) 进行了非常简短的了解,在我看来,我的网站将接受来自其他网站的登录。例如任何拥有 Gmail 或 LiveID 帐户的人都可以在
我需要向 IS 添加自定义权限和角色。此处提供用例 http://venurakahawala.blogspot.in/search/label/custom%20permissions .如何实现这
我有许多使用 .NET 成员身份和表单例份验证的旧版 .NET Framework Web 应用程序。他们每个人都有自己的登录页面,但都在同一个域中(例如.mycompany.com),共享一个 AS
我是一名优秀的程序员,十分优秀!