oldcodefork/laminas-api-tools-oauth2

Laminas 模块,用于实现 OAuth2 服务器

1.0.0 2023-02-06 09:21 UTC

This package is auto-updated.

Last update: 2024-09-06 12:40:32 UTC


README

Build Status

🇷🇺 俄罗斯公民

我们,Laminas 的成员,来自不同的国家。我们中许多人既有在俄罗斯的朋友、亲属和同事,也有在乌克兰的。我们中的一些人出生在俄罗斯。我们中的一些人目前居住在俄罗斯。我们中的一些人的祖父母在二战中与法西斯战斗过。在这里,没有人支持法西斯主义。

我们中的一员有一位乌克兰亲属,她和儿子一起逃离了家园。由于前方道路上的轰炸,火车延误了。我们有朋友躲在防空洞里。我们在空袭后焦急地等待他们的消息,空袭无差别地轰炸医院、学校、幼儿园和住宅。我们不从任何媒体上获得这些信息。我们亲眼目睹了这一切。

您信任我们足够使用我们的程序,我们请求您再次信任我们。我们需要帮助。走出家门,抗议这场毫无意义的战争。停止血腥。说“停止战争!”

🇺🇸 致俄罗斯公民

Laminas 的我们来自世界各地。我们中许多人既有在俄罗斯的朋友、家人和同事,也有在乌克兰的。我们中的一些人出生在俄罗斯。我们中的一些人目前居住在俄罗斯。我们中的一些人的祖父母在二战中与纳粹战斗过。在这里,没有人支持法西斯主义。

我们的团队成员中有一位乌克兰亲属,她和儿子一起逃离了家园。由于前方道路上的轰炸,火车延误了。我们有朋友躲在防空洞里。我们在空袭后焦急地等待他们的消息,空袭无差别地轰炸医院、学校、幼儿园和住宅。我们不从任何媒体上获得这些信息。这是我们亲身的经历。

您信任我们足够使用我们的软件。我们请求您信任我们说出真相。我们需要您的帮助。走出家门,抗议这场毫无必要的战争。停止杀戮。说“停止战争!”

Laminas 模块,用于 OAuth2 认证。

此模块使用 Brent Shaffer 的 oauth2-server-php 库来提供 OAuth2 支持。

要求

请参阅 composer.json 文件。

安装

您可以使用以下方式安装

$ composer require laminas-api-tools/api-tools-oauth2

如果您使用 ext/mongodb,您还需要安装兼容包

$ composer require alcaeus/mongo-php-adapter

最后,您需要将以下模块添加到应用程序的配置中

'modules' => [
    /* ... */
    'Laminas\ApiTools\ApiProblem',
    'Laminas\ApiTools\ContentNegotiation',
    'Laminas\ApiTools\OAuth2',
],

laminas-component-installer

如果您使用 laminas-component-installer,该插件将为您安装 api-tools-oauth2 和其其他 Laminas API Tools 依赖项作为模块。

配置

此模块使用任何 PDO 支持的数据库来管理 OAuth2 信息(用户、客户端、令牌等)。数据库结构存储在 data/db_oauth2.sql 中。

CREATE TABLE oauth_clients (
    client_id VARCHAR(80) NOT NULL,
    client_secret VARCHAR(80) NOT NULL,
    redirect_uri VARCHAR(2000) NOT NULL,
    grant_types VARCHAR(80),
    scope VARCHAR(2000),
    user_id VARCHAR(255),
    CONSTRAINT clients_client_id_pk PRIMARY KEY (client_id)
);
CREATE TABLE oauth_access_tokens (
    access_token VARCHAR(40) NOT NULL,
    client_id VARCHAR(80) NOT NULL,
    user_id VARCHAR(255),
    expires TIMESTAMP NOT NULL,
    scope VARCHAR(2000),
    CONSTRAINT access_token_pk PRIMARY KEY (access_token)
);
CREATE TABLE oauth_authorization_codes (
    authorization_code VARCHAR(40) NOT NULL,
    client_id VARCHAR(80) NOT NULL,
    user_id VARCHAR(255),
    redirect_uri VARCHAR(2000),
    expires TIMESTAMP NOT NULL,
    scope VARCHAR(2000),
    id_token VARCHAR(2000),
    CONSTRAINT auth_code_pk PRIMARY KEY (authorization_code)
);
CREATE TABLE oauth_refresh_tokens (
    refresh_token VARCHAR(40) NOT NULL,
    client_id VARCHAR(80) NOT NULL,
    user_id VARCHAR(255),
    expires TIMESTAMP NOT NULL,
    scope VARCHAR(2000),
    CONSTRAINT refresh_token_pk PRIMARY KEY (refresh_token)
);
CREATE TABLE oauth_users (
    username VARCHAR(255) NOT NULL,
    password VARCHAR(2000),
    first_name VARCHAR(255),
    last_name VARCHAR(255),
    CONSTRAINT username_pk PRIMARY KEY (username)
);
CREATE TABLE oauth_scopes (
    type VARCHAR(255) NOT NULL DEFAULT "supported",
    scope VARCHAR(2000),
    client_id VARCHAR (80),
    is_default SMALLINT DEFAULT NULL
);
CREATE TABLE oauth_jwt (
    client_id VARCHAR(80) NOT NULL,
    subject VARCHAR(80),
    public_key VARCHAR(2000),
    CONSTRAINT jwt_client_id_pk PRIMARY KEY (client_id)
);

PostgreSQL

我们还在 data/db_oauth2_postgresql.sql 中提供了一个特定于 PostgreSQL 的 DDL。

出于安全原因,我们使用 bcrypt 算法(通过类 Laminas\Crypt\Password\Bcrypt)加密字段 client_secret(表 oauth_clients)和 password(表 oauth_users)。

为了配置数据库访问的 api-tools-oauth2 模块,您需要将文件 config/oauth2.local.php.dist 复制到您的 Laminas 应用程序的 config/autoload/oauth2.local.php 中,并编辑它以提供您的数据库凭证(DNS、用户名、密码)。

我们还提供了一个 SQLite 数据库 data/dbtest.sqlite,您可以在测试环境中使用。在这个数据库中,您将找到一个具有 client_id "testclient" 和 client_secret "testpass" 的测试客户端账户。如果您想使用此数据库,您可以将您的 config/autoload/oauth2.local.php 文件配置如下

return array(
    'api-tools-oauth2' => array(
        'db' => array(
            'dsn' => 'sqlite:<path to api-tools-oauth2 module>/data/dbtest.sqlite',
        ),
    ),
);

Mongo 配置

Mongo OAuth2 适配器通过添加与 api-tools 其余部分相同的密码加密来封装 bshaffer 适配器。所需的集合与上面的 PDO 适配器中的相同。要创建 OAuth2 客户端,请在 oauth_clients 集合中插入以下文档

{
    "client_id":     "testclient",
    "client_secret": "$2y$14$f3qml4G2hG6sxM26VMq.geDYbsS089IBtVJ7DlD05BoViS9PFykE2",
    "redirect_uri":  "/oauth/receivecode",
    "grant_types":   null
}

用户 ID 提供者

当用户请求授权码时,他们可能将用户 ID 作为请求参数提供给 /oauth/authorize 路由。这将随着用户通过 oauth2 流程,将 user_id 存储在 access_tokenrefresh_tokenauthorization_code 表中。

用户可以通过 Laminas\Authentication\AuthenticationService 或其他认证方式来认证。当用户在访问 /oauth/authorize 路由之前必须提供认证时,应使用已认证的用户 ID。这可以通过服务管理器别名 Laminas\ApiTools\OAuth2\Provider\UserId 来完成。

默认的用户 ID 提供者使用请求查询参数 user_id,并由 Laminas\ApiTools\OAuth2\Provider\UserId\Request 类处理。

此存储库提供了一个替代提供者,Laminas\ApiTools\OAuth2\Provider\UserId\AuthorizationService,它使用 Laminas\Authentication\AuthenticationService 来获取身份。要将用户 ID 提供者更改为使用此服务,将 Laminas\ApiTools\OAuth2\Provider\UserId 服务别名更改为指向它

return array(
    'service_manager' => 
        'aliases' => array(
            'Laminas\ApiTools\OAuth2\Provider\UserId' => 'Laminas\ApiTools\OAuth2\Provider\UserId\AuthenticationService',
        ),
    ),
);

如何测试 OAuth2

要测试 OAuth2 模块,您必须向 oauth2 数据库添加一个 client_id 和一个 client_secret。如果您正在使用 SQLite 测试数据库,则不需要添加 client_id;只需使用默认的 "testclient"/"testpass" 账户即可。

由于我们使用 bcrypt 算法加密密码,您需要使用 Laminas 的 Laminas\Crypt\Password\Bcrypt 类来加密密码。我们提供了一个简单的脚本 /bin/bcrypt.php 来生成用户密码的散列值。您可以从命令行使用此工具,语法如下

php bin/bcrypt.php testpass

其中 "testpass" 是您想要加密的用户密码。上一条命令的输出将是用户密码的散列值,一个 60 字节的字符串,如下所示

$2y$14$f3qml4G2hG6sxM26VMq.geDYbsS089IBtVJ7DlD05BoViS9PFykE2

在生成密码的散列值(client_secret)后,您可以使用以下 SQL 语句在数据库中添加一个新的 client_id

INSERT INTO oauth_clients (
    client_id,
    client_secret,
    redirect_uri)
VALUES (
    "testclient",
    "$2y$14$f3qml4G2hG6sxM26VMq.geDYbsS089IBtVJ7DlD05BoViS9PFykE2",
    "/oauth/receivecode"
);

要测试 OAuth2 模块,您可以使用 HTTP 客户端,如 HTTPieCURL。下面的示例使用 HTTPie 和测试账户 "testclient"/"testpass"。

请求令牌(客户端凭据)

您可以使用以下 HTTPie 命令请求 OAuth2 令牌

http --auth testclient:testpass -f POST http://<URL of your Laminas app>/oauth grant_type=client_credentials

此 POST 请求使用 client_credentials 模式从 OAuth2 服务器请求新的令牌。这在机器到机器交互中用于应用程序访问。如果一切正常,您应该收到如下响应

{
    "access_token":"03807cb390319329bdf6c777d4dfae9c0d3b3c35",
    "expires_in":3600,
    "token_type":"bearer",
    "scope":null
}

安全提示:由于此 POST 使用基本 HTTP 认证,client_secret 将在 HTTP 请求中以明文形式暴露。为了保护此调用,需要一个 TLS/SSL 连接。

授权(代码)

如果您需要将OAuth2服务与Web应用程序集成,您需要使用授权代码授予类型。这种授权类型需要一步审核步骤来授权Web应用程序。这一步是通过一个简单的表单实现的,该表单请求用户批准对资源(账户)的访问。此模块提供了一个简单的表单来授权特定的客户端。该表单可以通过以下URL由浏览器访问:

http://<URL of your Laminas app>/oauth/authorize?response_type=code&client_id=testclient&redirect_uri=/oauth/receivecode&state=xyz

此页面将渲染一个表单,要求用户授权或拒绝客户端的访问。如果他们授权访问,OAuth2模块将回复一个授权代码。此代码必须用于请求OAuth2令牌;以下HTTPie命令提供了一个如何做到这一点的示例:

http --auth testclient:testpass -f POST http://<URL of your Laminas app>/oauth grant_type=authorization_code&code=YOUR_CODE&redirect_uri=/oauth/receivecode

在客户端场景(即移动端)中,如果您无法以安全方式存储客户端凭据,则不能使用之前的流程。在这种情况下,我们可以使用一个隐式授权。这与授权代码类似,但授权请求返回的不是授权代码,而是一个令牌

要使模块能够接受隐式授权类型,您需要将config/autoload/oauth2.local.php文件中的allow_implicit配置更改为true

return array(
    'api-tools-oauth2' => array(
        // ...
        'allow_implicit' => true,
        // ...
    ),
);

要从客户端请求令牌,您需要通过OAuth2服务器请求授权。

http://<URL of your Laminas app>/oauth/authorize?response_type=token&client_id=testclient&redirect_uri=/oauth/receivecode&state=xyz

此请求将渲染与上一个示例类似的授权表单。如果授权访问,请求将被重定向到/oauth/receivecode(如上面示例中的redirect_uri参数所提供),并且URI片段中指定了access_token,如下所示:

/oauth/receivecode#access_token=559d8f9b6bedd8d94c8e8d708f87475f4838c514&expires_in=3600&token_type=Bearer&state=xyz

要获取access_token,您可以通过解析URI来获取。我们使用URI片段传递access_token,因为这样令牌不会被传输到服务器;它将只对客户端可用。

在JavaScript中,您可以使用以下代码片段轻松解析URI:

// function to parse fragment parameters
var parseQueryString = function( queryString ) {
    var params = {}, queries, temp, i, l;

    // Split into key/value pairs
    queries = queryString.split("&");

    // Convert the array of strings into an object
    for ( i = 0, l = queries.length; i < l; i++ ) {
        temp = queries[i].split('=');
        params[temp[0]] = temp[1];
    }
    return params;
};

// get token params from URL fragment
var tokenParams = parseQueryString(window.location.hash.substr(1));

撤销(代码)

从版本1.4.0开始,您可以撤销访问令牌。默认情况下,撤销通过向路径/oauth/revoke的POST请求进行,该路径期望带有以下有效负载:

  • token,要撤销的OAuth2访问令牌。
  • token_type_hint => 'access_token',表示正在撤销访问令牌。

有效负载可以是application/x-www-form-urlencoded或JSON格式。

访问测试资源

当您获得有效的令牌时,您可以访问受限的API资源。OAuth2模块附带了一个可通过URL /oauth/resource 访问的测试资源。这是一个简单的资源,返回JSON数据。

要访问测试资源,您可以使用以下HTTPie命令:

http -f POST http://<URL of your Laminas app>/oauth/resource access_token=000ab5afab4cbbbda803fb9e50e7943f5e766748
# or
http http://<<URL of your Laminas app>/oauth/resource "Authorization:Bearer 000ab5afab4cbbbda803fb9e50e7943f5e766748"

如您所见,OAuth2模块支持通过POST(使用access_token值)或使用Bearer授权头(如RFC6750所示)来访问数据。

如何使用OAuth2保护您的API

您可以使用以下代码(例如,在控制器顶部)来保护您的API:

if (!$this->server->verifyResourceRequest(OAuth2Request::createFromGlobals())) {
    // Not authorized return 401 error
    $this->getResponse()->setStatusCode(401);
    return;
}

其中$this->serverOAuth2\Server的实例(请参阅AuthController.php)。