Skip to content
Merged
279 changes: 17 additions & 262 deletions packages/auth/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,289 +3,50 @@
> [!IMPORTANT]
> This repository is a read-only mirror of the [utopia-php monorepo](https://github.com/utopia-php/monorepo). Development happens in [`packages/auth`](https://github.com/utopia-php/monorepo/tree/main/packages/auth) — please open issues and pull requests there.

[![Build Status](https://travis-ci.org/utopia-php/auth.svg?branch=master)](https://travis-ci.org/utopia-php/auth)
![Total Downloads](https://img.shields.io/packagist/dt/utopia-php/auth.svg)
[![Discord](https://img.shields.io/discord/564160730845151244?label=discord)](https://appwrite.io/discord)

Utopia Auth library is a simple and lite library for handling authentication and authorization in PHP applications. This library provides a collection of secure hashing algorithms and authentication proofs for building robust authentication systems. This library is maintained by the [Appwrite team](https://appwrite.io).
Utopia Auth is a simple, dependency-free PHP library for building authentication and authorization: secure password hashing, authentication proofs (tokens, codes, phrases), and signing/verifying OAuth2 and OpenID Connect JWTs. It is maintained by the [Appwrite team](https://appwrite.io).

Although this library is part of the [Utopia Framework](https://github.com/utopia-php/framework) project it is dependency free and can be used as standalone with any other PHP project or framework.
Although it is part of the [Utopia Framework](https://github.com/utopia-php/framework) project, it is dependency free and can be used standalone with any PHP project or framework.

## Getting Started

Install using composer:

```bash
composer require utopia-php/auth
```

## System Requirements

Utopia Framework requires PHP 8.0 or later. We recommend using the latest PHP version whenever possible.

## Features

### Supported Hashing Hashes

- **Argon2** - Modern, secure, and recommended password hashing algorithm
- **Bcrypt** - Well-established and secure password hashing
- **Scrypt** - Memory-hard password hashing algorithm
- **ScryptModified** - Modified version of Scrypt with additional features
- **SHA** - Various SHA hash implementations
- **PHPass** - Portable password hashing framework
- **MD5** (Not recommended for passwords, legacy support only)

### Token Issuers

A generic framework for minting signed [JWS](https://datatracker.ietf.org/doc/html/rfc7515) tokens. The base `Issuer` is **not** tied to any particular protocol — it owns the JWS mechanics (header assembly, `jti` generation, base64url encoding and the header/payload/signature structure) and delegates only the signing algorithm and claim set to a subclass.

## Usage

### Data Store

```php
<?php

use Utopia\Auth\Store;

// Create a new store
$store = new Store();

// Set various types of data
$store->set('userId', '12345')
->set('name', 'John Doe')
->set('isActive', true)
->set('preferences', ['theme' => 'dark', 'notifications' => true]);

// Get values with optional defaults
$userId = $store->get('userId');
$missing = $store->get('missing', 'default value');

// Encode store data to a base64 string
$encoded = $store->encode();

// Later, decode the string back into a store
$newStore = new Store();
$newStore->decode($encoded);

// Access the decoded data
echo $newStore->get('name'); // Outputs: John Doe
```

### Password Hashing

```php
<?php

require_once __DIR__ . '/vendor/autoload.php';

use Utopia\Auth\Proofs\Password;
use Utopia\Auth\Hashes\Argon2;
use Utopia\Auth\Hashes\Bcrypt;

// Initialize password authentication with default algorithms
$password = new Password();

// Hash a password (uses Argon2 by default)
$hash = $password->hash('user-password');

// Verify the password
$isValid = $password->verify('user-password', $hash);

// Use a specific algorithm with custom parameters
$bcrypt = new Bcrypt();
$bcrypt->setCost(12); // Increase cost factor for better security

$password->setHash($bcrypt);
$hash = $password->hash('user-password');
```

### Authentication Tokens

```php
<?php

use Utopia\Auth\Proofs\Token;

// Generate secure authentication tokens
$token = new Token(32); // 32 characters length
$authToken = $token->generate(); // Random token
$hashedToken = $token->hash($authToken); // Store this in database

// Later, verify the token
$isValid = $token->verify($authToken, $hashedToken);
```

### One-Time Codes

```php
<?php

use Utopia\Auth\Proofs\Code;

// Generate verification codes (e.g., for 2FA)
$code = new Code(6); // 6-digit code
$verificationCode = $code->generate();
$hashedCode = $code->hash($verificationCode);

// Verify the code
$isValid = $code->verify($verificationCode, $hashedCode);
```

### Human-Readable Phrases

```php
<?php

use Utopia\Auth\Proofs\Phrase;

// Generate memorable authentication phrases
$phrase = new Phrase();
$authPhrase = $phrase->generate(); // e.g., "Brave cat"
$hashedPhrase = $phrase->hash($authPhrase);

// Verify the phrase
$isValid = $phrase->verify($authPhrase, $hashedPhrase);
```

### Advanced Hash Configuration

```php
<?php

use Utopia\Auth\Hashes\Scrypt;
use Utopia\Auth\Hashes\Argon2;

// Configure Scrypt parameters
$scrypt = new Scrypt();
$scrypt
->setCpuCost(16) // CPU/Memory cost parameter
->setMemoryCost(14) // Memory cost parameter
->setParallelCost(2) // Parallelization parameter
->setLength(64) // Output length in bytes
->setSalt('randomsalt123'); // Custom salt

// Configure Argon2 parameters
$argon2 = new Argon2();
$argon2
->setMemoryCost(65536) // Memory cost in KiB
->setTimeCost(4) // Number of iterations
->setThreads(3); // Number of threads
```

### Issuing Tokens

#### OAuth2 Access Tokens (RFC 9068)

```php
<?php

use Utopia\Auth\Issuers\Asymmetric\AccessToken;

// Generate an RSA key pair (do this once and persist the keys)
[$privateKey, $publicKey] = AccessToken::generateKeyPair();

$accessToken = new AccessToken(
$privateKey,
$publicKey,
'https://example.com/v1/oauth2/my-app' // The "iss" claim (authorization server)
);

// Issue a signed RS256 access token
$jwt = $accessToken->issue(
subject: 'user-123', // "sub" — the resource owner
audience: ['https://api.example.com'], // "aud" — the resource server
clientId: 'client-abc', // "client_id" — the client it was issued to
authTime: time(), // "auth_time" — when the user authenticated
duration: 3600, // Lifetime in seconds ("exp")
scopes: ['openid', 'profile', 'email']
);

$jwt = $accessToken->issue(
subject: 'user-123',
audience: ['https://api.example.com', 'https://mcp.example.com'],
clientId: 'client-abc',
authTime: time(),
duration: 3600,
scopes: ['openid', 'profile']
);

// Publish the public key as a JWK so resource servers can verify tokens
$jwk = $accessToken->getPublicJwk();
$keyId = $accessToken->getKeyId();
```

#### OAuth2 Refresh Tokens (HS256)

```php
<?php

use Utopia\Auth\Issuers\Symmetric\RefreshToken;

// Generate a signing secret (do this once and keep it server-side)
$secret = RefreshToken::generateSecret();

$refreshToken = new RefreshToken(
$secret,
'https://example.com/v1/oauth2/my-app'
);

// Issue a signed HS256 refresh token
$jwt = $refreshToken->issue(
subject: 'user-123', // "sub"
audience: 'https://example.com/v1/oauth2/token', // "aud" — the token endpoint
clientId: 'client-abc', // "client_id"
duration: 1209600, // Lifetime in seconds (e.g. 14 days)
scopes: ['openid', 'profile']
);
```

#### ID Tokens (OpenID Connect)

```php
<?php

use Utopia\Auth\Issuers\Asymmetric\IdToken;

[$privateKey, $publicKey] = IdToken::generateKeyPair();

$idToken = new IdToken(
$privateKey,
$publicKey,
'https://example.com/v1/oauth2/my-app'
);

// Issue a signed OIDC id_token
$jwt = $idToken->issue(
subject: 'user-123', // "sub" — the authenticated user
audience: 'client-abc', // "aud" — the client the token is for
authTime: time(), // "auth_time"
duration: 3600, // Lifetime in seconds ("exp")
nonce: 'n-0S6_WzA2Mj', // Optional "nonce" from the auth request
accessToken: $jwt, // Optional co-issued access_token (adds "at_hash")
code: null // Optional co-issued authorization code (adds "c_hash")
);
```

> Both asymmetric and symmetric issuers accept an optional `keyId` constructor argument (the JWS `kid` header) for key rotation. For asymmetric issuers it is derived deterministically from the public key when omitted.
## System Requirements

#### OAuth2 Resource Indicators (RFC 8707)
Utopia Auth requires PHP 8.1 or later. We recommend using the latest PHP version whenever possible.

```php
<?php
## Features

use Utopia\Auth\OAuth2\ResourceIndicators;
- **Password hashing** — Argon2, Bcrypt, Scrypt (and a modified Scrypt), SHA, PHPass, and MD5 (legacy only)
- **Authentication proofs** — cryptographically random tokens, one-time codes (e.g. 2FA), and human-readable phrases
- **Data store** — a base64-encodable key/value envelope for serializing authentication state
- **Token issuers** — mint signed [JWS](https://datatracker.ietf.org/doc/html/rfc7515): OAuth2 access tokens (RFC 9068), refresh tokens, and OpenID Connect id_tokens
- **Token verifiers** — verify RS256/HS256 JWS with an `alg`-confusion guard and standard claim checks
- **OAuth2 helpers** — RFC 8707 resource indicators

$resources = ResourceIndicators::from([
'https://api.example.com/',
'https://mcp.example.com/',
]);
$previouslyGrantedResources = ResourceIndicators::from(['https://api.example.com/']);
## Documentation

$isAllowed = $resources->isSubsetOf($previouslyGrantedResources);
$unchanged = $resources->equals($previouslyGrantedResources);
$audience = $resources->audience('https://cloud.example.com/v1/project');
$serialized = $resources->toArray();
```
- [Password Hashing](docs/hashing.md) — algorithms and tuning
- [Authentication Proofs](docs/proofs.md) — tokens, one-time codes, and phrases
- [Data Store](docs/store.md) — encode/decode authentication state
- [JSON Web Tokens](docs/jwt.md) — issuing and verifying OAuth2 / OpenID Connect tokens

## Tests

Expand All @@ -295,12 +56,6 @@ To run all unit tests, use the following Docker command:
docker compose exec tests vendor/bin/phpunit --configuration phpunit.xml tests
```

To run static code analysis, use the following command:

```bash
docker compose exec tests composer check
```

## Security

We take security seriously. If you discover any security-related issues, please email security@appwrite.io instead of using the issue tracker.
Expand Down
2 changes: 1 addition & 1 deletion packages/auth/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"test": "phpunit --configuration phpunit.xml"
},
"require": {
"php": ">=8.0",
"php": ">=8.1",
"ext-hash": "*",
"ext-openssl": "*",
"ext-scrypt": "*",
Expand Down
4 changes: 2 additions & 2 deletions packages/auth/composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading