goodid / goodid-php55-sdk
Requires
- php: >=5.5.9
- ext-openssl: *
- goodid/goodid-jose-php55: ^1.0
- jwadhams/json-logic-php: ^1.3
- paragonie/random_compat: ^1|^2
- spomky-labs/base64url: ^1.0
README
注意: 此版本支持 >= PHP 5.5.9。如果您使用的是 PHP 5.6 或更高版本,我们建议您使用https://github.com/idandtrust/goodid-php-sdk。
PHP 5.5 GoodID SDK
此仓库包含我们的开源 PHP SDK,允许您收集、解密和验证从用户那里接收到的数据。
安装
您可以使用Composer安装 GoodID PHP SDK。将 GoodID PHP SDK 包添加到您的 composer.json
文件中。
{ "require": { "goodid/goodid-php55-sdk": "~2.0" } }
前提条件
要为您的用户提供 GoodID 登录,您首先需要在 GoodID 上注册。您将获得 GoodID 移动应用测试版访问权限、GoodID 移动应用下载链接、客户端 ID、客户端密钥、默认密钥对和推荐的声明集。在此阶段,您还有机会生成自己的密钥对并将公钥发送到 GoodID。
GoodID 登录流程
这是对 GoodID 登录流程的简要介绍,让您了解在“要实现的端点”部分中将要实现的端点的目的。
GoodID 登录流程概述
- 当用户点击“使用 GoodID 登录”按钮时,GoodID JavaScript SDK 通过 AJAX 从您的 GoodID 登录初始化端点 获取一个新的“OpenID 身份验证请求”。
- GoodID JavaScript SDK 使用收到的“授权请求”打开 GoodID 登录页面,用户使用手机登录。
- 用户被重定向到您的 重定向 URI(着陆页面),带有“code”和“state”参数,这些参数由 GoodID PHP SDK 收集、解密和验证用户提供的信息。
- 恭喜!您已获取所有请求的用户数据。您可以进行登录或注册过程(如果是第一次使用给定的主题标识符登录)。
要实现的端点
GoodID 登录初始化端点
所谓的 GoodID 登录初始化端点 是 GoodID 的一个指定端点。它类似于 OpenID Connect 登录初始化端点。目前它负责以下事情:生成 OpenID 身份验证请求,并使 GoodID App-Initiated 登录流程(从提供商屏幕登录等)成为可能。该端点应是一个单独的 PHP 文件(例如,goodid-endpoint.php),其内容类似于以下代码片段。您不需要处理 GET/POST 参数或编写响应,这一切都由代码片段中实例化的 GoodID 端点自动完成。
// GoodID Login Initiation Endpoint (e.g. goodid-endpoint.php) // Load the SDK and other dependencies require_once __DIR__ . '/vendor/autoload.php'; use GoodID\Authentication\GoodIDEndpointFactory; use GoodID\Exception\GoodIDException; use GoodID\Helpers\Key\RSAPrivateKey; use GoodID\Helpers\OpenIDRequestSource\OpenIDRequestObject; use GoodID\ServiceLocator; // -- Option 1 -- // You can use our default session data handler. // In this case you need to start the session first. session_start(); $serviceLocator = new ServiceLocator(); // -- Option 2 -- // Or you can add your own session data handler // by defining a class which implements \GoodID\Helpers\SessionDataHandlerInterface // Add that to the $serviceLocator. $serviceLocator->setSessionDataHandler(new CustomSessionDataHandler()); try { // Simply create and run our object, it will take care of everything GoodIDEndpointFactory::createGoodIDEndpoint( $serviceLocator, "YOUR-CLIENT-ID", new RSAPrivateKey("YOUR-SIG-PRIV-KEY-PEM-STRING"), new RSAPrivateKey("YOUR-ENC-PRIV-KEY-PEM-STRING"), new OpenIDRequestObject("YOUR-CLAIMS-JSON-STRING"), "YOUR-REDIRECT-URI" )->run(); } catch (GoodIDException $e) { error_log('Login initiation failed: ' . $e->getMessage()); http_response_code(500); echo "Internal Server Error"; }
重定向URI(着陆页)
您还必须处理用户的登录请求。当用户使用GoodID登录时,他们将被重定向到您所谓的重定向URI,并带有“code”和“state”参数。您可以使用以下代码片段类似的东西。在登录(尝试)后进行重定向(强烈推荐),因为它从HTTP请求URL中删除查询参数,为用户提供更干净的用户体验。它还使他们的浏览器历史记录免受长期过期的授权代码的困扰。
// Redirect URI / landing page require_once __DIR__ . '/vendor/autoload.php'; use GoodID\Authentication\GoodIDResponse; use GoodID\Exception\GoodIDException; use GoodID\Helpers\Key\RSAPrivateKey; use GoodID\ServiceLocator; // -- Option 1 -- // You can use our default session data handler. // In this case you need to start the session first. session_start(); $serviceLocator = new ServiceLocator(); // -- Option 2 -- // Or you can add your own session data handler // by defining a class which implements \GoodID\Helpers\SessionDataHandlerInterface // Add that to the $serviceLocator. $serviceLocator->setSessionDataHandler(new CustomSessionDataHandler()); // If there is a "code" parameter, it must be a login attempt if (filter_has_var(INPUT_GET, 'code') || filter_has_var(INPUT_GET, 'error')) { try { // The GoodIDResponse object collects, decrypts and verifies the response data $response = new GoodIDResponse( $serviceLocator, "YOUR-CLIENT-ID", "YOUR-CLIENT-SECRET", new RSAPrivateKey("YOUR-SIG-PRIV-KEY-PEM-STRING"), new RSAPrivateKey("YOUR-ENC-PRIV-KEY-PEM-STRING") ); if($response->hasError()) { $error = $gidResponse->getError(); $errorDescription = $gidResponse->getErrorDescription(); // The login has failed with an OpenID Authentication Error Response // For example the user pressed cancel in the app } else { // Subject identifier $subjectIdentifier = $response->getSub(); // The data provided by the user $claims = $response->getClaims()->toArray(); // For debugging: // echo "Sub: $subjectIdentifier\n"; // echo "Claims: "; // print_r($claims); // exit; // Now begins the substantial part of the job: // You can do your custom validation of claims. // You can log in (or register) the user: // Read/write your DB, regenerate session id, etc. // Good luck :-) } } catch (GoodIDException $e) { // The login has failed with an exception // The identity of the user cannot be verified error_log('Login failed: ' . $e->getMessage()); } header('Location: /'); exit; }
向GoodID发送自定义验证错误
当GoodID应用程序认为某些数据有效,但您的自定义验证认为它无效时,您可能希望通知我们。有了这些信息,我们可以使验证更好,或者帮助您调整您的claimset以获得更好的结果。您可以通过以下方式将错误日志发送给我们
use GoodID\Helpers\Logger\RemoteLogger; use GoodID\Helpers\Logger\Log; use GoodID\ServiceLocator; // Assume $response is a GoodIDResponseObject, as seen in "Redirect URI (Landing page)" // And assume you have validated phone_number and billto.phone_number and they both had some errors. try { if($response->hasAccessToken()) { $logger = new RemoteLogger( $response->getAccessToken(), (new ServiceLocator())->getServerConfig() ); $logger->log("phone_number", "Data does not conform to ...", Log::LEVEL_ERROR); $logger->log("billto.phone_number", "Data does not conform to ...", Log::LEVEL_ERROR); $logger->send(); } } catch (GoodIDException $e) { error_log('Remote logging failed: ' . $e->getMessage()); }
杂项
这些步骤可能在集成的某些步骤中很有用。
生成自己的密钥对
如果您希望生成自己的密钥对,以下是如何操作的,使用openssl在Ubuntu上安装openssl
sudo apt-get install openssl
生成密钥对
openssl genrsa -out client-enc_key.pem 2048
openssl rsa -in client-enc_key.pem -pubout > client-enc_key.pub
openssl genrsa -out client-sig_key.pem 2048
openssl rsa -in client-sig_key.pem -pubout > client-sig_key.pub
之后,请将以下内容发送给我们
- 新的公钥对(.pub文件),并请将其标记,以便我们知道它们是为哪个环境准备的
- 使用新的公钥创建的请求对象进行数字签名(根据下一段所述)
生成请求对象
作为集成的最后一步,您需要向GoodID发送一个经过签名的默认请求对象。GoodID将此发布到GoodID托管的“请求URI”,但为您创建的是唯一的。当从GoodID应用程序开始登录过程时,我们将从那里获取您签署的请求。它可以如下生成
use GoodID\Helpers\Key\RSAPrivateKey; use GoodID\Helpers\OpenIDRequestSource\OpenIDRequestObject; use GoodID\ServiceLocator; $requestObject = new OpenIDRequestObject("Your claimset as a JSON string"); $jwt = $requestObject->generateJwt( new RSAPrivateKey("The content of your sig.pem as a string"), "Your client id", "Your default redirect URI", (new ServiceLocator())->getServerConfig() ); echo $jwt;
生成JWKs URI的内容
您的JWKs URI是一个端点,它包含一个JWKSet,该JWKSet是一组以JSON格式表示的JSON Web密钥。您必须将您的签名和加密公钥存储在JWKs URI上,以便在应用启动的登录流程(例如,提供商屏幕)中轻松可用。
如果您还没有JWKs URI,您可以像这样生成其内容
use GoodID\Helpers\Key\JwkSetGenerator; use GoodID\Helpers\Key\RSAPublicKey; $sigKey = new RSAPublicKey("The content of your sig.pub as a string"); $encKey = new RSAPublicKey("The content of your enc.pub as a string"); $jwkSetGenerator = new JwkSetGenerator(); $jwksUriContent = $jwkSetGenerator->generateJwksUriContent($sigKey, $encKey);
如果您有一个现有的JWKs URI,并且您想向其中添加更多密钥,您可以按照以下描述进行操作。$jwksUriContent
将包含您现有JWKs URI中的旧密钥以及通过参数传递的新密钥。
use GoodID\Helpers\Key\JwkSetGenerator; use GoodID\Helpers\Key\RSAPublicKey; $sigKey = new RSAPublicKey("The content of your sig.pub as a string"); $encKey = new RSAPublicKey("The content of your enc.pub as a string"); $jwkSetGenerator = new JwkSetGenerator(); $jwksUriContent = $jwkSetGenerator->generateJwksUriContent( $sigKey, $encKey, 'https://your-url.com/jwksuri.json' );
使用在picture_data
声明中返回的图片
这仅对您有意义,如果您已请求用户的照片。您不必将图像写入文件,它只是一个示例。
// Assume that $response is a GoodID response $claims = $response->getClaims(); $f = fopen('temporary.jpeg', 'wb'); fwrite($f, base64_decode($claims->get('picture_data'))); fclose($f);