Machine Installation id based Oauth 2

This commit is contained in:
2026-07-26 15:19:27 +05:30
parent dcb40473da
commit a4daf7e204
365 changed files with 23720 additions and 966 deletions

View File

@@ -0,0 +1,98 @@
# Cygnus cloud service
## Identity login API
`POST /api/v1/identity/login` requires a valid machine JWT with the
`identity.login` scope. The JWT must carry `client_id` and `installation_id`;
both must equal the values inside the encrypted payload.
The request uses a hybrid encrypted envelope:
```json
{
"keyId": "cygnus-login-2026-01",
"encryptedKey": "base64 RSA-OAEP-SHA256 encrypted AES key",
"initializationVector": "base64 12-byte AES-GCM IV",
"encryptedPayload": "base64 AES-GCM ciphertext and tag"
}
```
The AES-GCM additional authenticated data is the UTF-8 `keyId`. The decrypted
JSON is:
```json
{
"loginId": "user",
"password": "password",
"clientId": "client-id-from-jwt",
"installationId": "installation-id-from-jwt",
"nonce": "unique-random-value",
"issuedAt": "2026-07-23T06:30:00Z"
}
```
Configure the PKCS#8 RSA private key with
`CYGNUS_LOGIN_PRIVATE_KEY=file:/secure/path/login-private.pem`. Keep this key
outside the source tree and container image. The corresponding public key is
distributed to the on-prem gateway.
The database bootstrap is
`src/main/resources/db/identity/001_identity_login_schema.sql`. It is
transactional and idempotent; it copies login/menu data from `matrix.public`
to `matrix.identity`. It is intended for initial migration and controlled
development refreshes. Do not run it after `identity` becomes the production
system of record because its upserts intentionally refresh rows from `public`.
## Machine token endpoint
`POST /oauth2/token` implements the client-credentials flow used by the
on-premises gateway. The client assertion must be:
- an inner RS256 JWT signed with the installation private key;
- encrypted as RSA-OAEP-256 plus AES-256-GCM using the cloud assertion key;
- bound to the configured client ID, installation ID, and token audience;
- unexpired and no longer-lived than `CYGNUS_ASSERTION_TTL`.
The endpoint returns a short-lived RS256 access token carrying `client_id`,
`installation_id`, and the approved scope. The identity endpoint requires the
`identity.login` scope and verifies the same machine binding in the encrypted
login payload.
Generate separate cloud key pairs:
```bash
mkdir -p config/keys
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 \
-out config/keys/assertion-decryption-private.pem
openssl pkey -in config/keys/assertion-decryption-private.pem -pubout \
-out config/keys/assertion-decryption-public.pem
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 \
-out config/keys/access-token-private.pem
openssl pkey -in config/keys/access-token-private.pem -pubout \
-out config/keys/access-token-public.pem
chmod 600 config/keys/*private.pem
```
Configure clients in an external Spring YAML file rather than the packaged
`application.yml`:
```yaml
cygnus:
security:
enabled: true
issuer-uri: https://cloud.example.com
audience: cygnus-cloud-api
token-audience: https://cloud.example.com/oauth2/token
clients:
customer-a:
enabled: true
installation-id: site-01
assertion-public-key: file:/secure/clients/customer-a/public.pem
scopes:
- identity.login
```
Start with that protected file using
`--spring.config.additional-location=file:/secure/cygnus/clients.yml`.
Never place cloud private keys, customer assertions, or installation private
keys in the repository or container image.