Assent
多提供商认证框架。
特性
- 包含以下基础策略:
- OAuth 1.0 -
Assent.Strategy.OAuth
- OAuth 2.0 -
Assent.Strategy.OAuth2
- OpenID Connect -
Assent.Strategy.OIDC
- OAuth 1.0 -
- 包含以下提供商策略:
- Apple 登录 -
Assent.Strategy.Apple
- Auth0 -
Assent.Strategy.Auth0
- Azure AD -
Assent.Strategy.AzureAD
- Basecamp -
Assent.Strategy.Basecamp
- DigitalOcean -
Assent.Strategy.DigitalOcean
- Discord -
Assent.Strategy.Discord
- Facebook -
Assent.Strategy.Facebook
- Github -
Assent.Strategy.Github
- Gitlab -
Assent.Strategy.Gitlab
- Google -
Assent.Strategy.Google
- Instagram -
Assent.Strategy.Instagram
- LINE 登录 -
Assent.Strategy.LINE
- Linkedin -
Assent.Strategy.Linkedin
- Spotify -
Assent.Strategy.Spotify
- Strava -
Assent.Strategy.Strava
- Slack -
Assent.Strategy.Slack
- Stripe Connect -
Assent.Strategy.Stripe
- Twitter -
Assent.Strategy.Twitter
- VK -
Assent.Strategy.VK
- Apple 登录 -
安装
在 mix.exs
中将 Assent 添加到依赖列表:
defp deps do
[
# ...
{:assent, "~> 0.2.9"}
]
end
运行 mix deps.get
进行安装。
HTTP 客户端安装
默认情况下,如果您的依赖列表中有 Req
,则会使用它。否则,将使用 Erlang 的 :httpc
。
如果您使用 :httpc
,应该添加以下依赖项以启用 SSL 验证:
defp deps do
[
# ...
# 使用 :httpc 适配器时,需要进行 SSL 验证
{:certifi, "~> 2.4"},
{:ssl_verify_fun, "~> 1.1"}
]
end
您还必须在 mix.exs
中将 :inets
添加到 :extra_applications
:
def application do
[
# ...
extra_applications: [
# ...
:inets
]
]
end
如果您使用其他 HTTP 适配器(如 Req
或 Finch
),则无需进行此操作。
入门
策略包含两个阶段:请求和回调。在请求阶段,通常会将用户重定向到提供商进行认证,然后返回以启动回调阶段。
单一提供商示例
defmodule ProviderAuth do
import Plug.Conn
alias Assent.{Config, Strategy.Github}
@config [
client_id: "替换为客户端 ID",
client_secret: "替换为客户端密钥",
redirect_uri: "http://localhost:4000/auth/github/callback"
]
# http://localhost:4000/auth/github
def request(conn) do
@config
|> Github.authorize_url()
|> case do
{:ok, %{url: url, session_params: session_params}} ->
# 会话参数(用于 OAuth 2.0 和 OIDC 策略)将在用户返回回调阶段时被检索
conn = put_session(conn, :session_params, session_params)
```elixir
# 重定向终端用户到 Github 以授权访问其账户
conn
|> put_resp_header("location", url)
|> send_resp(302, "")
{:error, error} ->
# 生成请求授权 URL 时出现问题
end
end
# http://localhost:4000/auth/github/callback
def callback(conn) do
# 终端用户将返回到带有附加参数的回调 URL。
# 这些参数必须传递给策略。在此示例中,我们只
# 期望 GET 查询参数,但提供者也可能通过
# POST 请求返回用户,其中参数在 POST 正文中。
%{params: params} = fetch_query_params(conn)
# 请求阶段存储的会话参数(用于 OAuth 2.0 和 OIDC 策略)
# 将在回调阶段使用
session_params = get_session(conn, :session_params)
@config
# 应将会话参数添加到配置中,以便策略可以使用它们
|> Config.put(:session_params, session_params)
|> Github.callback(params)
|> case do
{:ok, %{user: user, token: token}} ->
# 授权成功
{:error, error} ->
# 授权失败
end
end
end
多提供者示例
这是一个通用流程,类似于 PowAssent 中使用的流程。
config :my_app, :strategies,
github: [
client_id: "替换为客户端ID",
client_secret: "替换为客户端密钥",
strategy: Assent.Strategy.Github
],
# ...
defmodule MultiProviderAuth do
alias Assent.Config
@spec request(atom()) :: {:ok, map()} | {:error, term()}
def request(provider) do
config = config!(provider)
config[:strategy].authorize_url()
end
@spec callback(atom(), map(), map()) :: {:ok, map()} | {:error, term()}
def callback(provider, params, session_params) do
config = config!(provider)
config
|> Assent.Config.put(:session_params, session_params)
|> config[:strategy].callback(params)
end
defp config!(provider) do
config =
Application.get_env(:my_app, :strategies)[provider] ||
raise "没有#{provider}的提供者配置"
Config.put(config, :redirect_uri, "http://localhost:4000/oauth/#{provider}/callback")
end
end
自定义提供者
你可以创建自定义策略。以下是使用 Assent.Strategy.OAuth2.Base
实现 OAuth 2.0 的示例:
defmodule TestProvider do
use Assent.Strategy.OAuth2.Base
@impl true
def default_config(_config) do
[
# `:base_url` 将用于以下任何路径
base_url: "http://localhost:4000/api/v1",
# 定义绝对 URI 会覆盖 `:base_url`
authorize_url: "http://localhost:4000/oauth/authorize",
token_url: "/oauth/access_token",
user_url: "/user",
authorization_params: [scope: "email profile"],
auth_method: :client_secret_post
]
end
@impl true
def normalize(_config, user) do
{:ok,
# 符合 https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.5.1
%{
"sub" => user["sub"],
"name" => user["name"],
"nickname" => user["username"],
"email" => user["email"]
# },
# # 不属于标准声明规范的提供者特定数据
# %{
# "http://localhost:4000/bio" => user["bio"]
}
}
end
end
规范化的用户映射应符合 OpenID Connect Core 1.0 标准声明规范,并应返回 {:ok, userinfo_claims}
或 {:ok, userinfo_claims, additional}
。用户信息声明中定义的任何不属于规范的键都不会包含在用户映射中。相反,它们应该设置在附加数据中,然后合并到用户信息声明之上,排除任何已经设置的键。
你可以使用 Assent.Strategy.OAuth2.Base
、Assent.Strategy.OAuth.Base
和 Assent.Strategy.OIDC.Base
宏来设置策略。
如果你需要比宏提供的更多控制权,可以使用 Assent.Strategy
行为来实现你的提供者:
defmodule TestProvider do
@behaviour Assent.Strategy
@spec authorize_url(Keyword.t()) :: {:ok, %{url: binary()}} | {:error, term()}
def authorize_url(config) do
# 生成授权 URL
end
@spec callback(Keyword.t(), map()) :: {:ok, %{user: map(), token: map()}} | {:error, term()}
def callback(config, params) do
# 处理回调响应
end
end
HTTP 客户端
Assent 默认支持 Req
、Finch
和 :httpc
。如果启用,默认使用 Req
HTTP 客户端适配器,否则将包含 Erlang 的 :httpc
适配器。
你可以在配置中显式设置 HTTP 客户端适配器:
config = [
client_id: "替换为客户端 ID",
client_secret: "替换为客户端密钥",
http_adapter: Assent.HTTPAdapter.Httpc
]
或在全局配置中设置:
config :assent, http_adapter: Assent.HTTPAdapter.Httpc
Req
Req 不需要任何额外配置,可以直接使用:
defp deps do
[
# ...
{:req, "~> 0.4"}
]
end
:httpc
如果 Req
不可用,将使用 Erlang 内置的 :httpc
进行请求。当 :certifi
和 :ssl_verify_fun
包可用时,会自动启用 SSL 验证。:httpc
仅支持 HTTP/1.1。
defp deps do
[
# ...
# 如果使用 `:httpc` 适配器,需要 SSL 验证
{:certifi, "~> 2.4"},
{:ssl_verify_fun, "~> 1.1"}
]
end
你必须将 :inets
添加到 :extra_applications
中,以在你的发布版本中包含 :httpc
。
Finch
Finch 需要在你的应用程序中有一个监督器。
更新 mix.exs
:
defp deps do
[
# ...
{:finch, "~> 0.16"}
]
end
确保在你的应用程序中启动 Finch 监督器,并在提供者配置中使用你的连接池设置 :http_adapter
:
config = [
client_id: "替换为客户端 ID",
client_secret: "替换为客户端密钥",
http_adapter: {Assent.HTTPAdapter.Finch, supervisor: MyFinch}
]
JWT 适配器
默认使用内置的 Assent.JWTAdapter.AssentJWT
进行 JWT 解析,但你可以使用自定义的 Assent.JWTAdapter
更改为任何第三方库。包含了一个 JOSE 适配器 Assent.JWTAdapter.JOSE
。
要使用 JOSE,更新 mix.exs
:
defp deps do
[
# ...
{:jose, "~> 1.8"}
]
end
并在提供者配置中传递 :jwt_adapter
:
config = [
client_id: "替换为客户端 ID",
client_secret: "替换为客户端密钥",
jwt_adapter: Assent.JWTAdapter.JOSE
]
或在全局配置中设置:
config :assent, jwt_adapter: AssAssent.JWTAdapter.JOSE
许可证
(MIT 许可证)
版权所有 (c) 2019-至今 Dan Schultzer 及贡献者
特此免费授予任何获得本软件副本和相关文档文件("软件")的人不受限制地处理本软件的权利,包括但不限于使用、复制、修改、合并、出版、发布、分发、再许可和/或销售软件副本的权利,以及允许向其提供软件的人这样做,但须符合以下条件:
上述版权声明和本许可声明应包含在软件的所有副本或主要部分中。
软件按"原样"提供,不提供任何形式的明示或暗示担保,包括但不限于对适销性、特定用途适用性和非侵权性的担保。在任何情况下,作者或版权持有人均不对任何索赔、损害或其他责任负责,无论是在合同诉讼、侵权行为或其他方面,由软件或软件的使用或其他交易引起的或与之相关的。