跳转至

身份验证流程 API

欢迎查阅身份验证流程 API 的 API 参考文档。本页面列出了该 API 支持的全部操作。

如需了解身份验证流程 API 的整体介绍,请参阅此文。

1.0 简介

本节 API 参考文档涵盖以下内容:

  • 端点
  • 如何向身份验证流程端点发出第一个 HTTP 请求。
  • 身份验证流程类型
  • 状态令牌的工作原理
  • API 输入与响应

1.1 端点

身份验证流程 API 有两个端点,分别是:

  • /api/v1/authentication_flows:该端点用于初始化新的身份验证流程,也可用于执行只需一步(一次 HTTP 请求)即可完成的流程。
  • /api/v1/authentication_flows/states/input:该端点要求请求体中包含 state_token 参数,其值必须是此前调用身份验证流程 API 时获得的有效状态令牌。

这两个端点都只支持通过 HTTPS 发送的 POST 请求,使用明文 HTTP 发送的请求会被拒绝。请求支持的内容类型为 application/json。

1.2 创建新的身份验证流程

使用身份验证流程 API 的第一步是创建一个新的身份验证流程。

什么是身份验证流程?

身份验证流程本质上是从向 /api/v1/authentication_flows 端点发起一次 HTTP 请求开始的。该端点会返回 state_token 等数据,以及一个 action 对象,用于决定下一步(请求)要执行的操作。action 对象的 type 属性(action.type)会返回不同的值,包括:identify、authenticate、verify、finish_redirect_url 等。

这一步至少需要向身份验证流程 API 端点发送一个请求体中包含 type 和 name 参数的 HTTP 请求。

HTTP 请求:

下面是创建一个 "type": "login" 新流程时 HTTP 请求的示例:

POST /api/v1/authentication_flows
Content-Type: application/json

{
  "type": "login",
  "name": "default"
}

上面的代码块展示了发送给身份验证流程 API 的典型 HTTP 请求应如何构造。下面详细说明请求的各个部分:

  • 该 API 要求通过 HTTPS 发送 POST 请求。
  • 需要请求的端点是 /api/v1/authentication_flows。完整端点示例为 https://{your-project.authanyid.com}/api/v1/authentication_flows,请将 {your-project.authanyid.com} 替换为您 Authany 项目的有效域名。
  • 请始终将请求的内容类型设置为 Content-Type: application/json,因为该 API 仅接受 JSON 格式的输入。
  • 包含 type 和 name 值的 JSON 对象随后可放入请求体中传递。
  • type 参数用于定义身份验证流程类型,name 参数可以设置为 "default"。

单步身份验证流程的 HTTP 请求:

下面展示的示例请求使用了手机号 + 密码登录方式的单步身份验证流程。

身份验证流程 API 支持您在 Authany 管理台为项目启用的多种登录方式。因此,若要使用下方示例中的单步身份验证流程,请确保已启用手机号 + 密码登录方式,且用户账号也已启用该方式。

POST /api/v1/authentication_flows
Content-Type: application/json

{
  "type": "login",
  "name": "default",
  "batch_input": [
        {
            "identification": "phone",
            "login_id": "+44123344556"
        },
        {
            "authentication": "primary_password",
            "password": "12345678"
        }
    ]
}

示例:

以下示例展示了如何在代码中向该 API 发起 HTTP 请求以启动登录流程:

const express = require('express');
const axios = require('axios');

function rawURLQuery(url) {
    const index = url.indexOf('?');
    return url.substr(index + 1);
}

const app = express();
const port = process.env.PORT || 3000;

const endpoint = "https://your-project.authanyid.com"; //replace with your actual Authgear project endpoint
async function userLogin(url_query) {
    const url = `${endpoint}/api/v1/authentication_flows?${url_query}`;

    const input = {
        "type": "login",
        "name": "default"
    };

    const headers = {
        "Content-Type": "application/json"
    }

    try {
        const startLogin = await axios.post(`${url}`, input, {
            headers: headers
        });

        return startLogin;

    }
    catch (error) {
        console.log(error.response);
        return error.response;

    }
}

app.get('/login', async (req, res) => {
    const url_query = rawURLQuery(req.url);
    const apiResponse = await userLogin(url_query);
    res.send(apiResponse.data);
});

app.listen(port, () => {
    console.log(`server started on port ${port}!`);
});
require 'vendor/autoload.php';
use GuzzleHttp\Client;

$endpoint = "https://your-project.authanyid.com"; //replace with your actual Authgear project endpoint
$url_params = $_SERVER['QUERY_STRING'];
$url = $endpoint . "/api/v1/authentication_flows" . "?" . $url_params;

$client = new Client();
$headers = [
    'Content-Type' => 'application/json',
    'Accept' => 'application/json',
];
$response = $client->post($url, [
    "json" => [
        'type' => 'login',
        'name' => 'default'
    ],
    "headers" => $headers
]);

var_dump($response->getBody());

注意:为了在身份验证流程结束时获取 finish_redirect_url,您必须在发起身份验证流程的初始 HTTP 请求中,包含 Authany 生成的完整 URL 查询参数。该 URL 查询参数是 Authany 授权服务器在将用户重定向到您的自定义身份验证界面时,添加到您的自定义界面 URI 上的一组参数。

1.3 身份验证流程类型

您可以通过 HTTP 请求体中的 type 参数指定要初始化的身份验证流程类型。

目前,该 API 的 type 参数支持以下三个值:

  • signup:新用户注册流程。
  • login:新用户登录流程。
  • signup_login:该流程会根据输入内容自动变为注册或登录。如果终端用户输入的是已存在的登录 ID,流程将变为登录;否则即为注册。
  • account_recovery:可使用该类型创建账号找回或密码重置等流程。

1.4 状态令牌

状态令牌可以帮助您跟踪身份验证流程的状态。API 端点的每个成功响应都会包含一个状态令牌。以下是 HTTP 响应中状态令牌的示例:

"result": {
    "state_token": "authflowstate_ABCD1234EF56GH8A0KFC998K",
    "type": "login",
    "name": "default",
    "action": {
            "type": "identify",
            "data": {
                "options": [
                    {
                        "identification": "email"
                    }
                ]
            }
        }
}

您可以在下一次 HTTP 请求的请求体中带上这个状态令牌,以继续该流程。下面的示例请求使用了上面示例中的状态令牌,用于继续 login 类型流程的下一步。

HTTP 请求:

POST /api/v1/authentication_flows/states/input
Content-Type: application/json

{
    "state_token": "authflowstate_ABCD1234EF56GH8A0KFC998K",
    "input": {
        "identification": "email",
        "login_id": "user@example.com"
    }
}

示例:

const express = require('express');
const axios = require('axios');

const app = express();
const port = process.env.PORT || 3000;

const endpoint = "https://your-project.authanyid.com"; //replace with your actual Authgear project endpoint
async function submitEmailUserLogin(email, state_token) {

    const url = `${endpoint}/api/v1/authentication_flows/states/input`;

    const input = {
        "state_token": state_token,
        "input": {
            "identification": "email",
            "login_id": email
        }
    };

    const headers = {
        "Content-Type": "application/json"
    }

    try {
        const submitEmail = await axios.post(`${url}`, input, {
            headers: headers
        });

        return submitEmail;

    }
    catch (error) {
        console.log(error.response);
        return error.response;

    }
}

app.get('/loginStep2', async (req, res) => {
    const apiResponse = await submitEmailUserLogin("pius@oursky.com", "ENTER_VALID_AUTH_STATE");
    res.send(apiResponse.data);
});

app.listen(port, () => {
    console.log(`server started on port ${port}!`);
});
require 'vendor/autoload.php';
use GuzzleHttp\Client;

$endpoint = "https://your-project.authanyid.com"; //replace with your actual Authgear project endpoint

$url = $endpoint . "/api/v1/authentication_flows/states/input";

$client = new Client();
$headers = [
    'Content-Type' => 'application/json',
    'Accept' => 'application/json',
];
$response = $client->post($url, [
    "json" => [
        "state_token" => "ENTER_VALID_AUTH_STATE",
        "input" => [
            "identification" => "email",
            "login_id" => "user@example.com"
        ]
    ],
    "headers" => $headers
]);

var_dump($response->getBody());

1.5 输入

身份验证流程 API 支持在 HTTP 请求体中以 JSON 形式传入输入内容。type、name、state_token 等特殊字段可以直接作为请求体参数传递,而用户输入等其他值则必须通过 input 或 batch_input 字段传递。

如果要传递会改变身份验证流程状态的输入内容,您必须向 /api/v1/authentication_flows/states/input 端点发起 HTTP 请求,而不是 /api/v1/authentication_flows。

下面给出了带有输入内容的请求结构:

HTTP 请求:

POST /api/v1/authentication_flows/states/input
Content-Type: application/json

{
    "state_token": "authflowstate_ABCD1234EF56GH8A0KFC998K",
    "batch_input":
            [
                {
                    "identification": "email",
                    "login_id": "user@example.com"
                },
                {
                    "authentication": "primary_password",
                    "password": "12Hjdusd@o*qfhs$"
                }
            ]
}

从上面的结构可以看到,state_token 参数是请求体中的直接参数,而邮箱地址、密码等用户输入则作为数组元素放在 batch_input 参数中。

用于传递邮箱和密码的键名是 Authany 指定的特殊字段,通常您可以在身份验证流程上一步的响应中找到这些键名。

1.6 响应

身份验证流程 API 的 HTTP 响应结构始终保持一致,也就是说响应结构总是相同的。响应格式为 JSON。

成功的响应会返回 200 HTTP 状态码,响应体结构如下:

{
    "result": {
        "state_token": "authflowstate_ABCD1234EF56GH8A0KFC998K",
        "type": "login",
        "name": "default",
        "action": {
            "type": "authenticate",
            "data": {
                "options": [
                    {
                        "authentication": "primary_password"
                    }
                ],
                "device_token_enabled": false
            }
        }
    }
}
  • state_token:指向身份验证流程当前这一状态的令牌。
  • type:身份验证流程的类型,可能的取值为 signup、login 和 signup_login。
  • name:身份验证流程的名称。
  • action:包含基于当前请求得到的下一步信息的对象。例如,如果当前请求只发送了 identification 信息(如邮箱),那么 action.type 将为 authenticate,action.data 将包含已启用身份验证方式的信息。
  • action.data.device_token_enabled:当该字段的值为 true 时,您可以在下一次输入中发送 request_device_token: true 以跳过双重验证。这一操作等同于在 AuthUI 中勾选"在此设备上下次跳过双重验证"复选框。Authany 会将设备令牌存储在浏览器 Cookie 中,以便日后跳过双重验证。

1.6.1 错误响应

当请求失败时,身份验证流程 API 会返回 400 范围内的 HTTP 状态码。以下是错误响应体的示例:

{
  "error": {
    "name": "Unauthorized",
    "reason": "InvalidCredentials",
    "message": "invalid credentials",
    "code": 401,
    "info": {}
  }
}
  • reason:可使用该字符串区分不同的错误。请勿使用 message 字段,因为它的内容可能会随时间变化。
  • info:包含错误附加信息的对象,该字段可能不存在(即不是 null,而是完全缺失)。

2.0 操作(action.type)

本节将详细介绍注册流程和登录流程支持的所有操作类型(action.type)。

2.1 action.type: identify(注册)

这通常包含在 type: signup 身份验证流程第一步的响应中。

HTTP 请求:

POST /api/v1/authentication_flows
Content-Type: application/json

{
  "type": "signup",
  "name": "default"
}

发起上述 HTTP 请求创建注册流程后,您会得到如下响应:

{
  "result": {
    "state_token": "authflowstate_5R6NM7HGGKV64538R0QEGY9RQBDM4PZD",
    "type": "signup",
    "name": "default",
    "action": {
      "type": "identify",
      "data": {
        "options": [
          {
            "identification": "email"
          },
          {
            "identification": "phone"
          },
          {
            "identification": "oauth",
            "oauth_provider_type": "google",
            "oauth_provider_alias": "google"
          },
          {
            "identification": "oauth",
            "oauth_provider_type": "wechat",
            "oauth_provider_alias": "wechat_mobile",
            "wechat_app_type": "mobile"
          }
        ]
      }
    }
  }
}

响应中的 action.data.options 对象包含一个数组,列出所有支持的身份标识方式。例如,出现 { "identification": "email" } 表示您可以使用邮箱地址来标识用户,从而继续上述注册流程。

您可以选择其中一个选项,并将其包含在身份验证流程的下一步(请求)中,如下所示:

"input": {
        "identification": "email",
        "login_id": "user@example.com"
    }

2.2 action.type: create_authenticator

该操作要求界面创建一个新的身份验证器。例如,您可以使用 create_authenticator 操作在创建账号(注册)过程中为新用户设置新密码。

响应:

{
    "result": {
        "state_token": "authflowstate_CCB9PT4EMVDKWKG7KW0ZVBG6JZ28DCK3",
        "type": "signup",
        "name": "default",
        "action": {
            "type": "create_authenticator",
            "data": {
                "options": [
                    {
                        "authentication": "primary_password",
                        "password_policy": {
                            "minimum_length": 8,
                            "history": {
                                "enabled": false
                            }
                        }
                    }
                ]
            }
        }
    }
}

从上面的响应可以看到,action.data.options 表明可以使用 primary_password 身份验证方式。要了解 Authany 支持的所有身份验证器,请参阅身份验证部分。

要为新用户设置主密码,请在下一次请求中发送以下输入:

"input": {
  "authentication": "primary_password",
  "new_password": "12Hjdusd@o*qfhs$"
}

发送上述输入后,将完成使用 primary_password 身份验证方式的注册流程。因此,下一次请求响应中的 action.type 值将为 "finished"。

2.3 action.type: identify(登录)

如果您在创建 type: login 的身份验证流程时未定义任何 input 或 batch_input,HTTP 请求的响应中将包含 action.type:identify。

以下是创建登录流程的简单 HTTP 请求结构。

HTTP 请求:

POST /api/v1/authentication_flows
Content-Type: application/json

{
  "type": "login",
  "name": "default"
}

上述 HTTP 请求的响应如下所示:

{
    "result": {
        "state_token": "authflowstate_30AQP46AD1DR219C0SBRNP5NG066R8YG",
        "type": "login",
        "name": "default",
        "action": {
            "type": "identify",
            "data": {
                "options": [
                    {
                        "identification": "email"
                    },
                    {
                        "identification": "oauth",
                        "oauth_provider_type": "wechat",
                        "oauth_provider_alias": "wechat_mobile",
                        "wechat_app_type": "mobile"
                    }
                ]
            }
        }
    }
}

action.data.options 包含所有支持的身份标识方式列表。

您可以使用其中一个选项("identification": "email"),通过 input 参数将其传入下一次请求,以继续登录流程,如下所示:

"input": {
        "identification": "email",
        "login_id": "user@example.com"
        }

2.4 action.type: authenticate

在请求体中传入身份标识选项的这一步骤,其响应会返回 actiontype.type:authenticate。包含该操作的响应如下所示:

{
    "result": {
        "state_token": "authflowstate_BAQ271GXCR3NNMR8DW3HTCWNET7TCQR8",
        "type": "login",
        "name": "default",
        "action": {
            "type": "authenticate",
            "data": {
                "options": [
                    {
                        "authentication": "primary_password"
                    }
                ],
                "device_token_enable": false
            }
        }
    }
}

上述响应中的 action.data.options 对象包含所有支持的身份验证器。可以看到,在上面的示例中只支持 primary_password 身份验证器。要了解 Authany 支持的所有身份验证器,请查看身份验证部分。

您可以通过 input 参数在下一次请求中传入身份验证器的值(用户的主密码),如下所示:

{
    "state_token": "authflowstate_J4SW4PGVXSZXS95NJAP8F67VZ31V4D0D",
    "input": {
        "authentication": "primary_password",
        "password": "12345678"
    }
}

对于仅使用主密码的简单登录,发送上述请求后将返回 action.type: finished,表示身份验证流程已完成。

2.5 action.type: verify

包含该操作的响应可能如下所示:

{
  "result": {
    "state_token": "authflowstate_PZMX4FG4N82WGSSY0Y398YH0F9BX4FPX",
    "type": "signup",
    "name": "default",
    "action": {
      "type": "verify",
      "data": {
        "channels": [
          "sms",
          "whatsapp"
        ]
      }
    }
  }
}

要发送用于验证流程的一次性密码(OTP),请使用响应中返回的状态令牌,在下一次请求中发送以下输入:

"state_token": "authflowstate_PZMX4FG4N82WGSSY0Y398YH0F9BX4FPX",
"input": {
  "channel": "sms"
}

上述请求的响应应如下所示:

{
  "result": {
    "state_token": "authflowstate_ABC123DE45FG1",
    "type": "signup",
    "name": "default",
    "action": {
      "type": "verify",
      "data": {
        "channel": "email",
        "otp_form": "code",
        "masked_claim_value": "john******@example.com",
        "code_length": 6,
        "can_resend_at": "2023-09-21T00:00:00+08:00",
        "can_check": false,
        "failed_attempt_rate_limit_exceeded": false
      }
    }
  }
}

如果 otp_form 为 code,系统会向 masked_claim_value 对应的终端用户发送一次性密码。

要重新发送一次性密码,请在新的一步中发送带有以下输入的请求:

"state_token": "authflowstate_ABC123DE45FG1",
"input": {
  "resend": true
}

接下来,请在请求中使用以下输入提交发送给用户的一次性密码:

"state_token": "authflowstate_XYZ987AA44Y9",
"input": {
  "code": "000000"
}

如果 OTP 形式为链接,您将得到以下响应:

{
  "result": {
    "state_token": "authflowstate_IJK567UU1KL889J",
    "type": "signup",
    "name": "default",
    "action": {
      "type": "verify",
      "data": {
        "channel": "email",
        "otp_form": "link",
        "websocket_url": "wss://...",
        "masked_claim_value": "john******@example.com",
        "code_length": 32,
        "can_resend_at": "2023-09-21T00:00:00+08:00",
        "can_check": false,
        "failed_attempt_rate_limit_exceeded": false
      }
    }
  }
}
  • 如果 otp_form 为 link,can_check 初始值为 false,且 data 中会包含 websocket_url。您可以使用该 URL 连接 WebSocket,以监听链接被批准的事件。
  • 验证链接会通过 masked_claim_value 中的邮箱发送给终端用户。用户点击链接后,将打开默认的批准页面。
  • 用户批准链接后,系统会发送一条内容为 JSON 对象 {"type": "refresh"} 的 WebSocket 消息。收到该消息后,您可以重新获取状态。
  • 获取到的状态中 can_check 应为 true。

现在,您可以传入以下输入来检查链接是否已被批准:

"state_token": "authflowstate_IJK567UU1KL889J",
"input": {
  "check": true
}

2.6 action.type: change_password

包含该操作的响应如下所示:

{
  "result": {
    "state_token": "authflowstate_MNO876HH96SFF6",
    "type": "login",
    "name": "default",
    "action": {
      "type": "change_password",
      "data": {
        "password_policy": {
          "minimum_length": 8,
          "alphabet_required": true,
          "digit_required": true
        }
      }
    }
  }
}

要提交新密码,请在请求中发送以下输入:

{
  "new_password": "a.new.password.that.meet.the.password.policy"
}

2.7 action.type: reset_password

以下是包含该操作的响应示例:

{
  "result": {
    "state_token": "authflowstate_5R6NM7HGGKV64538R0QEGY9RQBDM4PZD",
    "type": "signup",
    "name": "default",
    "action": {
      "type": "reset_password",
      "data": {
        "password_policy": {
          "minimum_length": 8,
          "digit_required": true,
          "history": {
            "enabled": false
          }
        }
      }
    }
  }
}

要完成密码重置流程,请传入以下输入:

{
  "new_password": "a.new.password.that.meet.the.password.policy"
}

2.8 完成响应

这是身份验证流程结束时返回的一种特殊响应。

示例:

{
  "result": {
    "state_token": "authflowstate_XYZ01234EF56GH8A0KFC998K",
    "type": "login",
    "name": "default",
    "action": {
      "type": "finished",
      "data": {
        "finish_redirect_uri": "https://myapp.authanyid.com/..."
      }
    }
  }
}
  • 由于这是身份验证流程的终点,完成响应中 action.type 的值为 "finished"。
  • action.data.finish_redirect_uri 中包含一个 URL,可用于将身份验证过程交还给 Authany,以便由 Authany 将用户重定向回客户端应用。只需在自定义身份验证界面中重定向到 action.data.finish_redirect_uri 中的 URL,即可将身份验证过程交还给 Authany。可参阅我们的示例:使用 finish_redirect_uri 完成注册流程。

3.0 输入:身份标识

本节将介绍所有支持的身份标识选项,以及如何将它们作为输入传递。

3.1 identification: email

响应体中的呈现方式:

{
  "identification": "email"
}

作为输入时的用法:

{
  "identification": "email",
  "login_id": "user@example.com"
}

3.2 identification: phone

响应体中的呈现方式:

{
  "identification": "phone"
}

作为输入时的用法

{
  "identification": "phone",
  "login_id": "+852980005432"
}

注意:手机号码必须为 E.164 格式,且不含任何分隔符或空格。

3.3 identification: username

响应体中的呈现方式:

{
  "identification": "username"
}

作为输入时的用法:

{
  "identification": "username",
  "login_id": "johndoe"
}

3.4 identification: oauth

响应中的呈现方式:

{
  "identification": "oauth",
  "oauth_provider_type": "google",
  "oauth_provider_alias": "google"
}
  • oauth_provider_type:OAuth 提供方的类型,可能的取值为:
  • google
  • facebook
  • github
  • linkedin
  • azureadv2
  • azureadb2c
  • adfs
  • apple
  • wechat
  • oauth_provider_alias:OAuth 提供方配置的别名,请将该值作为 alias 传入输入中。

作为输入时的用法:

{
  "identification": "oauth",
  "alias": "google",
  "redirect_uri": "<https://example.com/oauth/redirect/google>"
}

使用上述输入向身份验证流程 API 发起请求后,响应应如下所示:

{
    "result": {
        "state_token": "authflowstate_DVYQP8QXHDXYBNXGPPQZHM0YG3RC4SMH",
        "type": "signup",
        "name": "default",
        "action": {
            "type": "identify",
            "identification": "oauth",
            "data": {
                "oauth_provider_type": "google",
                "oauth_provider_alias": "google",
                "oauth_authorization_url": "https://accounts.google.com/o/oauth2/v2/auth?client_id=850158775140-cuov06p5ru3kq27n4bfvrlp3fb7vrpgd.apps.googleusercontent.com&prompt=select_account&redirect_uri=http%3A%2F%2Flocalhost%3A3000&response_type=code&scope=openid+profile+email"
            }
        }
    }
}

要继续该流程,请使用 action.data.oauth_authorization_url 中的链接,将用户重定向到 OAuth 提供方的授权页面。

用户在 OAuth 提供方网站上成功为您的应用授权后,会获得一个授权码。该授权码通常由提供方以 code 查询参数的形式返回在您的 OAuth 重定向 URL 中。

接下来,请在下一次发送给身份验证流程 API 的 HTTP 请求中,通过输入传递 OAuth 提供方添加到您重定向 URI 上的授权码和完整查询字符串,如下所示:

{
  "code": "<AUTHORIZATION CODE FROM OAUTH PROVIDER>",
  "query": "<COMPLETE QUERY STRING FROM THE REDIRECT URL>"
}

在某些情况下,用户可能无法成功完成授权。此时,您可以改为在下一次 HTTP 请求中发送以下错误处理输入,从而更优雅地继续身份验证流程:

{
  "error": "{{ error }}",
  "error_description": "{{ error_description }}",
  "error_uri": "{{ error_uri }}"
}

error_description 和 error_uri 为可选字段。

3.5 identification: select_account

select_account 选项允许已经拥有活跃 Authany 会话的用户直接以该账号继续操作,而无需重新输入凭据。该选项只会出现在 login 和 signup_login 流程的 identify 步骤中,且仅当流程配置中列出了该选项,并且请求携带了符合条件的会话 Cookie 时才会出现。如果没有符合条件的会话,options 中就不会包含该选项。完整指南请参阅使用自定义界面实现单点登录延续。

响应中的呈现方式:

{
  "identification": "select_account",
  "display_name": "user@example.com",
  "user_id": "user_01J8ZC0M2N3P4Q5R6S7T8V9W0X"
}
  • display_name:与调用方会话绑定账号对应的可读名称,适合用于类似"以 user@example.com 身份继续"的按钮文案。与该 API 其他地方的 masked_display_name 不同,由于它标识的是调用方自己的会话,因此不会被遮蔽。
  • user_id:该选项对应用户的 ID,仅供参考,不会作为输入被接受。如果您的界面需要将该选项与自行解析得到的 login_hint 或 id_token_hint 进行比对,可以使用该字段。

作为输入时的用法:

{
  "identification": "select_account",
  "index": 0
}
  • index:select_account 条目在当前响应 options 数组中的位置。

如果在选项展示和输入提交之间会话发生了变化,该 API 会返回 SelectAccountSessionChanged 错误。

4.0 输入:身份验证方式

本节将介绍所有支持的身份验证方式(身份验证器),以及如何将它们作为输入传递。

4.1 authentication: primary_password

响应中的呈现方式:

{
  "authentication": "primary_password",
  "password_policy": {
    "minimum_length": 8
  }
}

password_policy 的值是一个包含所有密码要求的对象。以下是 password_policy 对象可能包含的所有属性:

{
  "minimum_length": 8,
  "uppercase_required": true,
  "lowercase_required": true,
  "alphabet_required": true,
  "digit_required": true,
  "symbol_required": true,
  "minimum_zxcvbn_score": 4
}

作为输入时的用法(登录流程):

{
  "authentication": "primary_password",
  "password": "some.very.secure.password"
}

作为输入时的用法(注册流程)

{
  "authentication": "primary_password",
  "new_password": "some.very.secure.password"
}

4.2 authentication: primary_oob_otp_email

响应中的呈现方式(登录流程):

{
  "authentication": "primary_oob_otp_email",
  "otp_form": "code",
  "masked_display_name": "john****@example.com",
  "channels": ["email"]
}

在上述响应中,otp_form 表示将发送的一次性密码类型,masked_display_name 表示一次性密码将发送到的邮箱地址,channels 表示您必须从中选择的可用渠道。

响应中的呈现方式(注册流程):

{
  "authentication": "primary_oob_otp_email"
}

作为输入时的用法(登录流程):

{
  "authentication": "primary_oob_otp_email",
  "index": 1,
  "channel": "email"
}

要引用该身份验证方式,请使用其在 options 数组中的索引。在注册流程中使用 primary_oob_otp_email 的下一步,是验证发送到用户邮箱的一次性密码。

您可以在登录流程下一次请求的输入中发送一次性密码来进行验证,如下所示:

"input": {
        "code": "<OTP CODE SENT TO USER VIA EMAIL>"
    }

作为输入时的用法(注册流程)

{
  "authentication": "primary_oob_otp_email"
}

4.3 authentication: primary_oob_otp_sms

响应中的呈现方式(登录流程):

{
  "authentication": "primary_oob_otp_sms",
  "otp_form": "code",
  "masked_display_name": "+8529876****",
  "channels": ["sms", "whatsapp"]
}

otp_form 表示将发送的一次性密码类型,masked_display_name 表示一次性密码将发送到的手机号码,channels 表示您必须从中选择的可用渠道。

响应中的呈现方式(注册流程):

{
  "authentication": "primary_oob_otp_sms"
}

作为输入时的用法(登录流程):

{
  "authentication": "primary_oob_otp_sms",
  "index": 2,
  "channel": "sms"
}

要引用该身份验证方式,请使用其在 options 数组中的索引。

在登录流程中实现 primary_oob_otp_sms 的后续步骤,与 primary_oob_otp_email 类似,主要区别在于 channel 从 email 变为 sms。

作为输入时的用法(注册流程):

{
  "authentication": "primary_oob_otp_sms"
}

4.4 authentication: secondary_password

响应中的呈现方式:

{
  "authentication": "secondary_password",
  "password_policy": {
    "minimum_length": 8
  }
}

作为输入时的用法(登录流程):

{
  "authentication": "secondary_password",
  "password": "some.very.secure.password"
}

作为输入时的用法(注册流程):

{
  "authentication": "secondary_password",
  "new_password": "some.very.secure.password"
}

4.5 authentication: secondary_oob_otp_email

响应中的呈现方式:

{
  "authentication": "secondary_oob_otp_email"
}

作为输入时的用法:

{
  "authentication": "secondary_oob_otp_email",
  "target": "user@example.com"
}

注意:target 可以(且通常)与终端用户用于登录的邮箱地址不同。

4.6 authentication: secondary_oob_otp_sms

响应中的呈现方式:

{
  "authentication": "secondary_oob_otp_sms"
}

作为输入时的用法:

{
  "authentication": "secondary_oob_otp_sms",
  "target": "+85298000032"
}

注意:target 必须为 E.164 格式,且不含任何分隔符或空格。它可以(且通常)与终端用户用于登录的手机号码不同。

4.7 authentication: secondary_totp

响应中的呈现方式:

{
  "authentication": "secondary_totp"
}

作为输入时的用法(登录流程)

{
  "authentication": "secondary_totp",
  "code": "<TOTP CODE>"
}

作为输入时的用法(注册流程):

{
  "authentication": "secondary_totp"
}

在注册流程请求的输入中发送 "authentication": "secondary_totp",将返回如下所示的响应:

{
    "result": {
        "state_token": "authflowstate_PS3KDB0DZQQ3QZRSM40XWMJPK9NQXZYQ",
        "type": "signup",
        "name": "default",
        "action": {
            "type": "create_authenticator",
            "authentication": "secondary_totp",
            "data": {
                "secret": "ABCDEFGHIJKLM9NOP08R2M7APSXYZ",
                "otpauth_uri": "otpauth://totp/pius1www2@example.com?algorithm=SHA1&digits=6&issuer=https%3A%2F%2Fyour-app.authanyid.com&period=30&secret=ABCDEFGHIJKLM9NOP08R2M7APSXYZ"
            }
        }
    }
}

您可以将 action.data.otpauth_uri 的值编码为二维码,供用户在身份验证器应用中扫描;也可以展示 action.data.secret 的值,供用户在身份验证器应用中手动输入密钥。

要完成 TOTP 身份验证流程,请在下一次请求中发送身份验证器应用生成的验证码,如下所示:

{
  "code": "<CODE FROM AUTHENTICATOR APP>"
}

5.0 常见问题

5.1 重新获取状态

您可以使用状态令牌获取现有状态的更多详情,方法是发起以下 HTTP 请求:

POST /api/v1/authentication_flows/states
Content-Type: application/json

{
  "state_token": "{{ state_token }}"
}

通常您不需要这样做,因为状态会在创建后或输入提交后自动返回。

6.0 错误处理

本节将介绍身份验证流程 API 如何处理各类错误。

当请求失败时,身份验证流程 API 会返回 400 范围内的 HTTP 状态码;当服务器发生内部错误时,则会返回相应的错误状态码。

根据发生错误的类型不同,身份验证流程 API 的错误响应中可能缺少某些字段。因此,我们建议您在应用逻辑中使用这些字段前,先在代码中进行判空检查。

6.1 error 对象。

当身份验证流程中发生错误时,身份验证流程 API 的主 HTTP 响应中可能会包含 error 字段。error 字段的值即为 error 对象。error 对象包含以下字段:

  • name:用于标记一组相关错误的简短标签。
  • reason:reason 字段的值可用于区分不同类型的错误,它比 name 字段更具体;由于 message 字段的值可能发生变化,reason 字段在应用逻辑中的使用也比 message 字段更可靠。
  • message:message 字段的值是一个字符串,包含关于所发生错误的更详细描述信息。
  • code:与错误关联的 HTTP 状态码,例如 500、400、401 和 404。
  • info:错误响应中可能不包含 info 字段。如果包含,其值是一个对象,包含关于所发生错误的更详细信息。下面详细说明 info 字段中的部分数据。

Error.info

error.info 对象可能包含以下字段:

  • FlowType:该字段说明发生错误的身份验证流程类型,例如 login 和 signup 流程。
  • causes:一个数组,包含当前错误所有可能来源的详细信息,以及解决该错误的提示。
  • cause:cause 的值是一个对象,包含说明当前错误发生原因的简要信息。

为了进一步理解身份验证流程 API 错误响应的结构,下面来看几个常见错误的示例,我们将根据 error.reason 对错误进行分类

6.2 ValidationFailed

当缺少必填字段时,error.reason 的值可能为 ValidationFailed。在下面的示例中,由于发送给身份验证流程 API /api/v1/authentication_flows/states/input 端点的请求缺少必填的 input 或 batch_input 字段,因此产生了该错误。

"error": {
        "name": "Invalid",
        "reason": "ValidationFailed",
        "message": "invalid request body",
        "code": 400,
        "info": {
            "causes": [
                {
                    "location": "",
                    "kind": "required",
                    "details": {
                        "actual": [
                            "state_token"
                        ],
                        "expected": [
                            "input"
                        ],
                        "missing": [
                            "input"
                        ]
                    }
                },
                {
                    "location": "",
                    "kind": "required",
                    "details": {
                        "actual": [
                            "state_token"
                        ],
                        "expected": [
                            "batch_input"
                        ],
                        "missing": [
                            "batch_input"
                        ]
                    }
                }
            ]
        }
    }

ValidationFailed 错误的另一个原因,是必填字段的值格式无效,或必填字段中包含无效的常量值。

无效常量的一个例子是将 identification 的值设置为允许取值(email、phone、oauth、username、select_account)之外的任何值。此外,如果使用的常量对应的功能不受当前登录方式支持,也可能引发同样的错误。您始终可以在 error.info 对象中找到关于错误原因的更详细信息。

以下示例展示了当用户输入的字符串不符合邮箱地址格式时出现的 ValidationFailed 错误:

"error": {
        "name": "Invalid",
        "reason": "ValidationFailed",
        "message": "invalid login ID",
        "code": 400,
        "info": {
            "FlowType": "signup",
            "causes": [
                {
                    "location": "/login_id",
                    "kind": "format",
                    "details": {
                        "format": "email"
                    }
                }
            ]
        }
    }

6.3 InvariantViolated

当注册流程的输入中包含另一个用户已存在的身份标识(邮箱、手机号或用户名)时,可能会发生 InvariantViolated 错误。您可以在 error.info 对象中找到关于 InvariantViolated 错误原因的更详细信息。

以下示例展示了注册流程中使用另一个用户已存在的邮箱注册新用户时的错误响应:

"error": {
        "name": "Invalid",
        "reason": "InvariantViolated",
        "message": "identity already exists",
        "code": 400,
        "info": {
            "FlowType": "signup",
            "IdentityTypeExisting": "login_id",
            "IdentityTypeIncoming": "login_id",
            "LoginIDTypeExisting": "email",
            "LoginIDTypeIncoming": "email",
            "cause": {
                "kind": "DuplicatedIdentity"
            }
        }
    }

6.4 PasswordPolicyViolated

当新密码的值不满足您 Authany 项目的最低密码要求时,身份验证流程 API 会返回 PasswordPolicyViolated 错误。关于请求中的密码未满足要求的具体原因,详见 error.info 对象。

在以下示例中,请求中的密码未达到 8 个字符的最小长度要求:

"error": {
    "name": "Invalid",
    "reason": "PasswordPolicyViolated",
    "message": "password policy violated",
    "code": 400,
    "info": {
        "FlowType": "signup",
        "causes": [
            {
                "Name": "PasswordTooShort",
                "Info": {
                    "min_length": 8,
                    "pw_length": 4
                }
            }
        ]
    }
}

6.5 AuthenticationFlowNotFound

当请求中的 state_token 无效或已过期时,error.reason 的值可能为 AuthenticationFlowNotFound。此类错误不包含 info 字段。

"error": {
        "name": "NotFound",
        "reason": "AuthenticationFlowNotFound",
        "message": "flow not found",
        "code": 404
    }

6.6 InvalidCredentials

当身份验证器的输入值无效(例如现有用户输入了错误的密码)导致身份验证失败时,身份验证流程 API 会返回 InvalidCredentials 错误。

关于身份验证器类型的更多详情,可在 error.info 对象中找到,如下所示:

"error": {
        "name": "Unauthorized",
        "reason": "InvalidCredentials",
        "message": "invalid credentials",
        "code": 401,
        "info": {
            "AuthenticationType": "password",
            "FlowType": "login"
        }
    }

6.7 UserNotFound

当用户身份标识(邮箱、手机号、用户名)的输入值在 Authany 项目中不存在时,会发生 UserNotFound 错误。换句话说,未找到与该邮箱、手机号或用户名对应的用户。

关于该错误的更多详情,可在 error.info 对象中找到。

"error": {
        "name": "NotFound",
        "reason": "UserNotFound",
        "message": "user not found",
        "code": 404,
        "info": {
            "FlowType": "login",
            "IdentityTypeIncoming": "login_id",
            "LoginIDTypeIncoming": ""
        }
    }

6.8 UnexpectedError

这是一种较为罕见的错误,可能由不规范的输入引起,例如格式错误的 JSON(如在 JSON 中包含多余的逗号)。

"error": {
        "name": "InternalError",
        "reason": "UnexpectedError",
        "message": "unexpected error occurred",
        "code": 500
    }

6.9 SelectAccountSessionChanged

当提交了 select_account 输入,但会话已不再对应此前提供的账号时,会发生 SelectAccountSessionChanged 错误,例如在两次请求之间,用户在另一个标签页中登出或切换了账号。处理方式是创建一个新的身份验证流程,并渲染最新的响应。

"error": {
        "name": "Unauthorized",
        "reason": "SelectAccountSessionChanged",
        "message": "session no longer matches the selected account",
        "code": 401
    }