diff --git a/README.md b/README.md index 2bf17dde..c1a7f99b 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,9 @@ different types of authentication: 1. `MittwaldAPIClient::newUnauthenticated()` 2. `MittwaldAPIClient::newWithToken(string $apiToken)` (recommended) -3. `MittwaldAPIClient::newWithCredentials(string $email, string $password)` (does - actually perform a login in the background; does not work when using 2FA) +3. `MittwaldAPIClient::newWithCredentials(string $email, string $password)`, does actually perform a login in the background; does not work when using 2FA. +4. `MittwaldAPIClient::newWithAccessTokenRetrievalKey(string $userId, string $key)`, authenticates an mStudio user using the [access token retrieval key][atrek] mechanism. Only useful if you're building an mStudio extension. +5. `MittwaldAPIClient::newWithExtensionSecret(string $extensionInstanceId, string $extensionSecret)`, authenticates an mStudio extension itself (without any user interaction). Only useful if you're building an mStudio extension. Have a look at our [API introduction][api-getting-started] for more information on how to obtain an API token and how to get started with the API. @@ -53,4 +54,5 @@ foreach ($listProjectResponse->getBody() as $project) { The API documentation can be found in our [Developer Portal][api-ref]. You can find the operation ID on the right side of each operation. [api-getting-started]: https://developer.mittwald.de/docs/v2/api/intro -[api-ref]: https://developer.mittwald.de/reference/v2/ \ No newline at end of file +[api-ref]: https://developer.mittwald.de/reference/v2/ +[atrek]: https://developer.mittwald.de/docs/v2/contribution/overview/concepts/authentication/ \ No newline at end of file diff --git a/src/MittwaldAPIV2Client.php b/src/MittwaldAPIV2Client.php index 9ef8e6b7..7f532b97 100644 --- a/src/MittwaldAPIV2Client.php +++ b/src/MittwaldAPIV2Client.php @@ -5,8 +5,13 @@ namespace Mittwald\ApiClient; use Mittwald\ApiClient\Generated\V2\ClientImpl; +use Mittwald\ApiClient\Generated\V2\Clients\Marketplace\ExtensionAuthenticateInstance\ExtensionAuthenticateInstanceRequest; +use Mittwald\ApiClient\Generated\V2\Clients\Marketplace\ExtensionAuthenticateInstance\ExtensionAuthenticateInstanceRequestBody; use Mittwald\ApiClient\Generated\V2\Clients\User\Authenticate\AuthenticateRequest; use Mittwald\ApiClient\Generated\V2\Clients\User\Authenticate\AuthenticateRequestBody; +use Mittwald\ApiClient\Generated\V2\Clients\User\AuthenticateWithAccessTokenRetrievalKey\AuthenticateWithAccessTokenRetrievalKeyRequest; +use Mittwald\ApiClient\Generated\V2\Clients\User\AuthenticateWithAccessTokenRetrievalKey\AuthenticateWithAccessTokenRetrievalKeyRequestBody; +use SensitiveParameter; /** * MittwaldAPIV2Client is the main entry point for the mStudio v2 API. @@ -24,7 +29,7 @@ class MittwaldAPIV2Client extends ClientImpl { private const DEFAULT_BASE_URL = 'https://api.mittwald.de/v2/'; - final protected function __construct(string $baseUri, string|null $apiKey = null) + final protected function __construct(string $baseUri, #[SensitiveParameter] string|null $apiKey = null) { parent::__construct($baseUri, $apiKey); } @@ -38,7 +43,7 @@ final protected function __construct(string $baseUri, string|null $apiKey = null * @param string $apiToken The API token * @param string $baseUri The base URL of the API. Defaults to the production API. Note that this is only useful for testing. */ - public static function newWithToken(string $apiToken, string $baseUri = self::DEFAULT_BASE_URL): static + public static function newWithToken(#[SensitiveParameter] string $apiToken, string $baseUri = self::DEFAULT_BASE_URL): static { return new static($baseUri, $apiToken); } @@ -67,11 +72,54 @@ public static function newUnauthenticated(string $baseUri = self::DEFAULT_BASE_U * @param string $password The password of your mStudio user. * @param string $baseUri The base URL of the API. Defaults to the production API. Note that this is only useful for testing. */ - public static function newWithCredentials(string $email, string $password, string $baseUri = self::DEFAULT_BASE_URL): static + public static function newWithCredentials(string $email, #[SensitiveParameter] string $password, string $baseUri = self::DEFAULT_BASE_URL): static { $authenticateRequest = new AuthenticateRequest(new AuthenticateRequestBody($email, $password)); $authenticateResponse = static::newUnauthenticated($baseUri)->user()->authenticate($authenticateRequest); return static::newWithToken($authenticateResponse->getBody()->getToken(), baseUri: $baseUri); } + + /** + * Instantiates a new client that is authenticated using an "access token retrieval key". + * + * This is a special mechanism for one-click-authenticating users that are + * redirected to an mStudio extension [1]. + * + * [1]: https://developer.mittwald.de/docs/v2/contribution/overview/concepts/authentication/ + * + * @param string $userId The ID of the user to authenticate. This value is passed as a parameter during the authentication flow. + * @param string $key The access token retrieval key. + * @param string $baseUri The base URL of the API. Defaults to the production API. Note that this is only useful for testing. + */ + public static function newWithAccessTokenRetrievalKey(string $userId, #[SensitiveParameter] string $key, string $baseUri = self::DEFAULT_BASE_URL): static + { + $authenticateRequest = new AuthenticateWithAccessTokenRetrievalKeyRequest(new AuthenticateWithAccessTokenRetrievalKeyRequestBody( + accessTokenRetrievalKey: $key, + userId: $userId, + )); + $authenticateResponse = static::newUnauthenticated($baseUri)->user()->authenticateWithAccessTokenRetrievalKey($authenticateRequest); + + return static::newWithToken($authenticateResponse->getBody()->getToken(), baseUri: $baseUri); + } + + /** + * Instantiates a client that is authenticated as an mStudio extension (not a user). + * + * @param string $extensionInstanceId The extension instance ID; this is passed to you via a webhook when the extension is installed. + * @param string $extensionSecret The extension instance secret; this is passed to you via a webhook when the extension is installed. + * @param string $baseUri The base URL of the API. Defaults to the production API. Note that this is only useful for testing. + */ + public static function newWithExtensionSecret(string $extensionInstanceId, #[SensitiveParameter] string $extensionSecret, string $baseUri = self::DEFAULT_BASE_URL): static + { + $authenticateRequest = new ExtensionAuthenticateInstanceRequest( + extensionInstanceId: $extensionInstanceId, + body: new ExtensionAuthenticateInstanceRequestBody( + extensionInstanceSecret: $extensionSecret, + ) + ); + $authenticateResponse = static::newUnauthenticated($baseUri)->marketplace()->extensionAuthenticateInstance($authenticateRequest); + + return static::newWithToken($authenticateResponse->getBody()->getPublicToken(), baseUri: $baseUri); + } }