Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@

.PHONY: test
test:
vendor/bin/phpstan analyze --level 6 src/ tests/ \
vendor/bin/phpstan analyze --memory-limit 256M --level 6 src/ tests/ \
&& vendor/bin/phpunit --do-not-cache-result tests/
115 changes: 97 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[![PHP Version Require](https://img.shields.io/packagist/php-v/capcom6/android-sms-gateway?style=for-the-badge)](https://packagist.org/packages/capcom6/android-sms-gateway)
[![Total Downloads](https://img.shields.io/packagist/dt/capcom6/android-sms-gateway.svg?style=for-the-badge)](https://packagist.org/packages/capcom6/android-sms-gateway)

A modern PHP client for seamless integration with the [SMS Gateway for Android](https://sms-gate.app) API. Send SMS messages, manage devices, and configure webhooks through your PHP applications with this intuitive library.
A modern PHP client for seamless integration with the [SMSGate](https://sms-gate.app) API. Send SMS messages, manage devices, and configure webhooks through your PHP applications with this intuitive library.

## 🔖 Table of Contents

Expand All @@ -17,8 +17,15 @@ A modern PHP client for seamless integration with the [SMS Gateway for Android](
- [🚀 Quickstart](#-quickstart)
- [Sending an SMS](#sending-an-sms)
- [Managing Devices](#managing-devices)
- [🔐 Authentication](#-authentication)
- [Basic Authentication](#basic-authentication)
- [JWT Authentication](#jwt-authentication)
- [Generating a JWT Token](#generating-a-jwt-token)
- [Using a JWT Token](#using-a-jwt-token)
- [Revoking a JWT Token](#revoking-a-jwt-token)
- [📚 Full API Reference](#-full-api-reference)
- [Client Initialization](#client-initialization)
- [Basic Authentication](#basic-authentication-1)
- [Core Methods](#core-methods)
- [Builder Methods](#builder-methods)
- [🔒 Security Notes](#-security-notes)
Expand All @@ -36,6 +43,8 @@ A modern PHP client for seamless integration with the [SMS Gateway for Android](
- **Error Handling**: Structured exception management
- **Type Safety**: Strict typing throughout the codebase
- **Encryption Support**: End-to-end message encryption
- **Dual Authentication**: Support for both Basic and JWT authentication
- **Token Management**: Generate, use, and revoke JWT tokens with configurable scopes and TTL

## ⚙️ Prerequisites

Expand Down Expand Up @@ -103,36 +112,106 @@ try {
}
```

## 🔐 Authentication

The SMSGate client supports two authentication methods: Basic Authentication and JWT (JSON Web Token) authentication. Each method has its own use cases and benefits.

### Basic Authentication

```php
// Initialize client with Basic authentication
$client = new Client('your_login', 'your_password');
```

### JWT Authentication

JWT authentication uses bearer tokens for authentication.

#### Generating a JWT Token

```php
use AndroidSmsGateway\Client;
use AndroidSmsGateway\Domain\TokenRequest;

// First, create a client with Basic authentication to generate a token
$basicClient = new Client('your_login', 'your_password');

// Create a token request with specific scopes and TTL
$tokenRequest = new TokenRequest(
['messages:send', 'messages:read'], // Scopes for permissions
3600 // Token TTL in seconds (optional)
);

// Generate the token
$tokenResponse = $basicClient->GenerateToken($tokenRequest);
$jwtToken = $tokenResponse->AccessToken();

echo "Token generated! Expires at: " . $tokenResponse->ExpiresAt() . PHP_EOL;
```

#### Using a JWT Token

```php
// Initialize client with JWT authentication
$jwtClient = new Client(null, $jwtToken);

// Now use the client as usual
$message = (new MessageBuilder('Your message text here.', ['+1234567890']))->build();
$messageState = $jwtClient->SendMessage($message);
```

#### Revoking a JWT Token

```php
// Revoke a token using its ID (jti)
$basicClient->RevokeToken($tokenResponse->ID());
echo "Token revoked successfully!" . PHP_EOL;
```

## 📚 Full API Reference

### Client Initialization

The client supports two authentication methods: Basic Authentication and JWT Bearer Tokens.

#### Basic Authentication
```php
$client = new Client(
string $login,
$clientBasic = new Client(
string $login,
string $password,
string $serverUrl = 'https://api.sms-gate.app/3rdparty/v1',
?\Psr\Http\Client\ClientInterface $httpClient = null,
?\AndroidSmsGateway\Encryptor $encryptor = null
);

$clientJWT = new Client(
null, // Set login to null for JWT
string $jwtToken, // JWT token as the second parameter
string $serverUrl = 'https://api.sms-gate.app/3rdparty/v1',
?\Psr\Http\Client\ClientInterface $httpClient = null,
?\AndroidSmsGateway\Encryptor $encryptor = null
);
```

### Core Methods

| Category | Method | Description |
| ------------ | ---------------------------------------------------- | --------------------------------- |
| **Messages** | `SendMessage(Message $message)` | Send SMS message |
| | `GetMessageState(string $id)` | Get message status by ID |
| | `RequestInboxExport(MessagesExportRequest $request)` | Request inbox export via webhooks |
| **Devices** | `ListDevices()` | List registered devices |
| | `RemoveDevice(string $id)` | Remove device by ID |
| **System** | `HealthCheck()` | Check API health status |
| | `GetLogs(?string $from, ?string $to)` | Retrieve system logs |
| **Settings** | `GetSettings()` | Get account settings |
| | `PatchSettings(Settings $settings)` | Partially update account settings |
| | `ReplaceSettings(Settings $settings)` | Replace account settings |
| **Webhooks** | `ListWebhooks()` | List registered webhooks |
| | `RegisterWebhook(Webhook $webhook)` | Register new webhook |
| | `DeleteWebhook(string $id)` | Delete webhook by ID |
| Category | Method | Description |
| ------------------ | ---------------------------------------------------- | --------------------------------- |
| **Messages** | `SendMessage(Message $message)` | Send SMS message |
| | `GetMessageState(string $id)` | Get message status by ID |
| | `RequestInboxExport(MessagesExportRequest $request)` | Request inbox export via webhooks |
| **Devices** | `ListDevices()` | List registered devices |
| | `RemoveDevice(string $id)` | Remove device by ID |
| **System** | `HealthCheck()` | Check API health status |
| | `GetLogs(?string $from, ?string $to)` | Retrieve system logs |
| **Settings** | `GetSettings()` | Get account settings |
| | `PatchSettings(Settings $settings)` | Partially update account settings |
| | `ReplaceSettings(Settings $settings)` | Replace account settings |
| **Webhooks** | `ListWebhooks()` | List registered webhooks |
| | `RegisterWebhook(Webhook $webhook)` | Register new webhook |
| | `DeleteWebhook(string $id)` | Delete webhook by ID |
| **Authentication** | `GenerateToken(TokenRequest $request)` | Generate a new JWT token |
| | `RevokeToken(string $jti)` | Revoke a JWT token by ID |

### Builder Methods
```php
Expand Down
57 changes: 52 additions & 5 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
use AndroidSmsGateway\Domain\Webhook;
use AndroidSmsGateway\Domain\MessagesExportRequest;
use AndroidSmsGateway\Domain\Settings;
use AndroidSmsGateway\Domain\TokenRequest;
use AndroidSmsGateway\Domain\TokenResponse;
use AndroidSmsGateway\Exceptions\HttpException;
use Http\Discovery\Psr17FactoryDiscovery;
use Http\Discovery\Psr18ClientDiscovery;
Expand All @@ -21,7 +23,7 @@ class Client {
public const DEFAULT_URL = 'https://api.sms-gate.app/3rdparty/v1';
public const USER_AGENT_TEMPLATE = 'android-sms-gateway/2.0 (client; php %s)';

protected string $basicAuth;
protected string $authHeader;
protected string $baseUrl;

protected ClientInterface $client;
Expand All @@ -31,13 +33,21 @@ class Client {
protected StreamFactoryInterface $streamFactory;

public function __construct(
string $login,
?string $login,
string $password,
string $serverUrl = self::DEFAULT_URL,
?ClientInterface $client = null,
?Encryptor $encryptor = null
) {
$this->basicAuth = base64_encode($login . ':' . $password);
if (!empty($login)) {
$this->authHeader = 'Basic ' . base64_encode($login . ':' . $password);
} elseif (!empty($password)) {
$passwordOrToken = $password;
$this->authHeader = 'Bearer ' . $passwordOrToken;
} else {
throw new RuntimeException('Missing credentials');
}

$this->baseUrl = $serverUrl;
$this->client = $client ?? Psr18ClientDiscovery::find();
$this->encryptor = $encryptor;
Expand Down Expand Up @@ -371,6 +381,42 @@ public function DeleteWebhook(string $id): void {
);
}

/**
* Generate a new JWT token
*
* @param TokenRequest $request
* @return TokenResponse
*/
public function GenerateToken(TokenRequest $request): TokenResponse {
$path = '/auth/token';

$response = $this->sendRequest(
'POST',
$path,
$request
);
if (!is_object($response)) {
throw new RuntimeException('Invalid response');
}

return TokenResponse::FromObject($response);
}

/**
* Revoke a JWT token
*
* @param string $jti
* @return void
*/
public function RevokeToken(string $jti): void {
$path = '/auth/token/' . $jti;

$this->sendRequest(
'DELETE',
$path
);
}

/**
* @param \AndroidSmsGateway\Interfaces\SerializableInterface|null $payload
* @throws \Http\Client\Exception\HttpException
Expand All @@ -390,8 +436,9 @@ protected function sendRequest(string $method, string $path, $payload = null) {
$method,
$this->baseUrl . $path
)
->withAddedHeader('Authorization', 'Basic ' . $this->basicAuth)
->withAddedHeader('User-Agent', sprintf(self::USER_AGENT_TEMPLATE, PHP_VERSION));
->withAddedHeader('User-Agent', sprintf(self::USER_AGENT_TEMPLATE, PHP_VERSION))
->withAddedHeader('Authorization', $this->authHeader);

if (isset($data)) {
$request = $request
->withAddedHeader('Content-Type', 'application/json')
Expand Down
67 changes: 67 additions & 0 deletions src/Domain/TokenRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

namespace AndroidSmsGateway\Domain;

use AndroidSmsGateway\Interfaces\SerializableInterface;

class TokenRequest implements SerializableInterface {
/** @var string[] */
private array $scopes;
private ?int $ttl;

/**
* @param string[] $scopes
* @param int|null $ttl
*/
public function __construct(array $scopes, ?int $ttl = null) {
$this->scopes = $scopes;
$this->ttl = $ttl;
}

/**
* @return string[]
*/
public function Scopes(): array {
return $this->scopes;
}

/**
* @param string[] $scopes
* @return self
*/
public function setScopes(array $scopes): self {
$this->scopes = $scopes;
return $this;
}

public function TTL(): ?int {
return $this->ttl;
}

public function setTtl(?int $ttl): self {
$this->ttl = $ttl;
return $this;
}

public function toObject(): \stdClass {
$obj = new \stdClass();
$obj->scopes = $this->scopes;

if ($this->ttl !== null) {
$obj->ttl = $this->ttl;
}

return $obj;
}

/**
* @param object $obj
* @return self
*/
public static function FromObject(object $obj): self {
return new self(
$obj->scopes ?? [],
$obj->ttl ?? null
);
}
}
Loading
Loading