diff --git a/apps/astro/.env.example b/apps/astro/.env.example
index f0bb6ccf..0ee2e20a 100644
--- a/apps/astro/.env.example
+++ b/apps/astro/.env.example
@@ -1,66 +1,96 @@
-# Opaque secret used to sign and verify JWT tokens
-
-# openssl rand -base64 32
+# Opaque salting and peppering secrets for key derivation and hashing
+# For example: openssl rand -base64 32
AURA_AUTH_SALT=""
-# openssl rand -base64 32
+
+# Opaque secret for signing JWT tokens and encrypting data
+# For example: openssl rand -base64 32
AURA_AUTH_SECRET=""
+# The base URL of the authentication server
+# For example: http://localhost:4000
+AURA_AUTH_BASE_URL=
+
+# Trusted Origins for CORS and redirect URI validation (comma-separated)
+# For example: http://localhost:3000,http://localhost:4000
+AURA_AUTH_TRUSTED_ORIGINS=
+
+# Enable debug logging for authentication operations (set to "true", "debug", "on", "yes" or "1" to enable)
+AURA_AUTH_DEBUG=
+
+# Log level for authentication operations (e.g. "error", "warn", "info", "debug")
+AURA_AUTH_LOG_LEVEL=
+
+# PEM-encoded RSA public/private key for signing and encrypting JWT tokens in "sealed" JWT mode (JWT with JWS and JWE)
+# For example: -----BEGIN PUBLIC KEY -----\n...\n-----END PUBLIC KEY-----
+AURA_AUTH_SIGNING_PUBLIC_KEY=
+AURA_AUTH_SIGNING_PRIVATE_KEY=
+AURA_AUTH_SIGNING_ALGORITHM=
+
+AURA_AUTH_ENCRYPTION_PUBLIC_KEY=
+AURA_AUTH_ENCRYPTION_PRIVATE_KEY=
+AURA_AUTH_ENCRYPTION_ALGORITHM=
+
+# PEM-encode RSA public/private key either in "signed" or "encrypted" JWT mode (JWT with JWS or JWE)
+AURA_AUTH_PUBLIC_KEY=
+AURA_AUTH_PRIVATE_KEY=
+AURA_AUTH_ALGORITHM=
+
# Github OAuth App Credentials
-AURA_AUTH_GITHUB_CLIENT_ID=""
-AURA_AUTH_GITHUB_CLIENT_SECRET=""
+AURA_AUTH_GITHUB_CLIENT_ID=
+AURA_AUTH_GITHUB_CLIENT_SECRET=
# Bitbucket OAuth App Credentials
-AURA_AUTH_BITBUCKET_CLIENT_ID=""
-AURA_AUTH_BITBUCKET_CLIENT_SECRET=""
+AURA_AUTH_BITBUCKET_CLIENT_ID=
+AURA_AUTH_BITBUCKET_CLIENT_SECRET=
# Figma OAuth App Credentials
-AURA_AUTH_FIGMA_CLIENT_ID=""
-AURA_AUTH_FIGMA_CLIENT_SECRET=""
+AURA_AUTH_FIGMA_CLIENT_ID=
+AURA_AUTH_FIGMA_CLIENT_SECRET=
# Discord OAuth App Credentials
-AURA_AUTH_DISCORD_CLIENT_ID=""
-AURA_AUTH_DISCORD_CLIENT_SECRET=""
+AURA_AUTH_DISCORD_CLIENT_ID=
+AURA_AUTH_DISCORD_CLIENT_SECRET=
# GitLab OAuth App Credentials
-AURA_AUTH_GITLAB_CLIENT_ID=""
-AURA_AUTH_GITLAB_CLIENT_SECRET=""
+AURA_AUTH_GITLAB_CLIENT_ID=
+AURA_AUTH_GITLAB_CLIENT_SECRET=
# Spotify OAuth App Credentials
-AURA_AUTH_SPOTIFY_CLIENT_ID=""
-AURA_AUTH_SPOTIFY_CLIENT_SECRET=""
+AURA_AUTH_SPOTIFY_CLIENT_ID=
+AURA_AUTH_SPOTIFY_CLIENT_SECRET=
# X (Twitter) OAuth App Credentials
-AURA_AUTH_X_CLIENT_ID=""
-AURA_AUTH_X_CLIENT_SECRET=""
+AURA_AUTH_X_CLIENT_ID=
+AURA_AUTH_X_CLIENT_SECRET=
# Strava OAuth App Credentials
-AURA_AUTH_STRAVA_CLIENT_ID=""
-AURA_AUTH_STRAVA_CLIENT_SECRET=""
+AURA_AUTH_STRAVA_CLIENT_ID=
+AURA_AUTH_STRAVA_CLIENT_SECRET=
# Atlassian OAuth App Credentials
-AURA_AUTH_ATLASSIAN_CLIENT_ID=""
-AURA_AUTH_ATLASSIAN_CLIENT_SECRET=""
+AURA_AUTH_ATLASSIAN_CLIENT_ID=
+AURA_AUTH_ATLASSIAN_CLIENT_SECRET=
# Mailchimp OAuth App Credentials
-AURA_AUTH_MAILCHIMP_CLIENT_ID=""
-AURA_AUTH_MAILCHIMP_CLIENT_SECRET=""
+AURA_AUTH_MAILCHIMP_CLIENT_ID=
+AURA_AUTH_MAILCHIMP_CLIENT_SECRET=
# Pinterest OAuth App Credentials
-AURA_AUTH_PINTEREST_CLIENT_ID=""
-AURA_AUTH_PINTEREST_CLIENT_SECRET=""
+AURA_AUTH_PINTEREST_CLIENT_ID=
+AURA_AUTH_PINTEREST_CLIENT_SECRET=
# Dropbox OAuth App Credentials
-AURA_AUTH_DROPBOX_CLIENT_ID=""
-AURA_AUTH_DROPBOX_CLIENT_SECRET=""
+AURA_AUTH_DROPBOX_CLIENT_ID=
+AURA_AUTH_DROPBOX_CLIENT_SECRET=
# Twitch OAuth App Credentials
-AURA_AUTH_TWITCH_CLIENT_ID=""
-AURA_AUTH_TWITCH_CLIENT_SECRET=""
+AURA_AUTH_TWITCH_CLIENT_ID=
+AURA_AUTH_TWITCH_CLIENT_SECRET=
# Notion OAuth App Credentials
-AURA_AUTH_NOTION_CLIENT_ID=""
-AURA_AUTH_NOTION_CLIENT_SECRET=""
+AURA_AUTH_NOTION_CLIENT_ID=
+AURA_AUTH_NOTION_CLIENT_SECRET=
# TikTok OAuth App Credentials
-AURA_AUTH_TIKTOK_CLIENT_ID=""
-AURA_AUTH_TIKTOK_CLIENT_SECRET=""
+AURA_AUTH_TIKTOK_CLIENT_ID=
+AURA_AUTH_TIKTOK_CLIENT_SECRET=
diff --git a/apps/bun/.env.example b/apps/bun/.env.example
index f5f67e6f..e4bc6cfe 100644
--- a/apps/bun/.env.example
+++ b/apps/bun/.env.example
@@ -1,67 +1,95 @@
-# Salt for key derivation (e.g., password hashing)
-# openssl rand -base64 32
+# Opaque salting and peppering secrets for key derivation and hashing
+# For example: openssl rand -base64 32
AURA_AUTH_SALT=""
-# Opaque secret used to sign and verify JWT tokens
-# openssl rand -base64 32
+# Opaque secret for signing JWT tokens and encrypting data
+# For example: openssl rand -base64 32
AURA_AUTH_SECRET=""
+# The base URL of the authentication server
+# For example: http://localhost:4000
+AURA_AUTH_BASE_URL=
+
+# Trusted Origins for CORS and redirect URI validation (comma-separated)
+# For example: http://localhost:3000,http://localhost:4000
+AURA_AUTH_TRUSTED_ORIGINS=
+
+# Enable debug logging for authentication operations (set to "true", "debug", "on", "yes" or "1" to enable)
+AURA_AUTH_DEBUG=
+
+# Log level for authentication operations (e.g. "error", "warn", "info", "debug")
+AURA_AUTH_LOG_LEVEL=
+
+# PEM-encoded RSA public/private key for signing and encrypting JWT tokens in "sealed" JWT mode (JWT with JWS and JWE)
+# For example: -----BEGIN PUBLIC KEY -----\n...\n-----END PUBLIC KEY-----
+AURA_AUTH_SIGNING_PUBLIC_KEY=
+AURA_AUTH_SIGNING_PRIVATE_KEY=
+AURA_AUTH_SIGNING_ALGORITHM=
+
+AURA_AUTH_ENCRYPTION_PUBLIC_KEY=
+AURA_AUTH_ENCRYPTION_PRIVATE_KEY=
+AURA_AUTH_ENCRYPTION_ALGORITHM=
+
+# PEM-encode RSA public/private key either in "signed" or "encrypted" JWT mode (JWT with JWS or JWE)
+AURA_AUTH_PUBLIC_KEY=
+AURA_AUTH_PRIVATE_KEY=
+AURA_AUTH_ALGORITHM=
# Github OAuth App Credentials
-AURA_AUTH_GITHUB_CLIENT_ID=""
-AURA_AUTH_GITHUB_CLIENT_SECRET=""
+AURA_AUTH_GITHUB_CLIENT_ID=
+AURA_AUTH_GITHUB_CLIENT_SECRET=
# Bitbucket OAuth App Credentials
-AURA_AUTH_BITBUCKET_CLIENT_ID=""
-AURA_AUTH_BITBUCKET_CLIENT_SECRET=""
+AURA_AUTH_BITBUCKET_CLIENT_ID=
+AURA_AUTH_BITBUCKET_CLIENT_SECRET=
# Figma OAuth App Credentials
-AURA_AUTH_FIGMA_CLIENT_ID=""
-AURA_AUTH_FIGMA_CLIENT_SECRET=""
+AURA_AUTH_FIGMA_CLIENT_ID=
+AURA_AUTH_FIGMA_CLIENT_SECRET=
# Discord OAuth App Credentials
-AURA_AUTH_DISCORD_CLIENT_ID=""
-AURA_AUTH_DISCORD_CLIENT_SECRET=""
+AURA_AUTH_DISCORD_CLIENT_ID=
+AURA_AUTH_DISCORD_CLIENT_SECRET=
# GitLab OAuth App Credentials
-AURA_AUTH_GITLAB_CLIENT_ID=""
-AURA_AUTH_GITLAB_CLIENT_SECRET=""
+AURA_AUTH_GITLAB_CLIENT_ID=
+AURA_AUTH_GITLAB_CLIENT_SECRET=
# Spotify OAuth App Credentials
-AURA_AUTH_SPOTIFY_CLIENT_ID=""
-AURA_AUTH_SPOTIFY_CLIENT_SECRET=""
+AURA_AUTH_SPOTIFY_CLIENT_ID=
+AURA_AUTH_SPOTIFY_CLIENT_SECRET=
# X (Twitter) OAuth App Credentials
-AURA_AUTH_X_CLIENT_ID=""
-AURA_AUTH_X_CLIENT_SECRET=""
+AURA_AUTH_X_CLIENT_ID=
+AURA_AUTH_X_CLIENT_SECRET=
# Strava OAuth App Credentials
-AURA_AUTH_STRAVA_CLIENT_ID=""
-AURA_AUTH_STRAVA_CLIENT_SECRET=""
+AURA_AUTH_STRAVA_CLIENT_ID=
+AURA_AUTH_STRAVA_CLIENT_SECRET=
# Atlassian OAuth App Credentials
-AURA_AUTH_ATLASSIAN_CLIENT_ID=""
-AURA_AUTH_ATLASSIAN_CLIENT_SECRET=""
+AURA_AUTH_ATLASSIAN_CLIENT_ID=
+AURA_AUTH_ATLASSIAN_CLIENT_SECRET=
# Mailchimp OAuth App Credentials
-AURA_AUTH_MAILCHIMP_CLIENT_ID=""
-AURA_AUTH_MAILCHIMP_CLIENT_SECRET=""
+AURA_AUTH_MAILCHIMP_CLIENT_ID=
+AURA_AUTH_MAILCHIMP_CLIENT_SECRET=
# Pinterest OAuth App Credentials
-AURA_AUTH_PINTEREST_CLIENT_ID=""
-AURA_AUTH_PINTEREST_CLIENT_SECRET=""
+AURA_AUTH_PINTEREST_CLIENT_ID=
+AURA_AUTH_PINTEREST_CLIENT_SECRET=
# Dropbox OAuth App Credentials
-AURA_AUTH_DROPBOX_CLIENT_ID=""
-AURA_AUTH_DROPBOX_CLIENT_SECRET=""
+AURA_AUTH_DROPBOX_CLIENT_ID=
+AURA_AUTH_DROPBOX_CLIENT_SECRET=
# Twitch OAuth App Credentials
-AURA_AUTH_TWITCH_CLIENT_ID=""
-AURA_AUTH_TWITCH_CLIENT_SECRET=""
+AURA_AUTH_TWITCH_CLIENT_ID=
+AURA_AUTH_TWITCH_CLIENT_SECRET=
# Notion OAuth App Credentials
-AURA_AUTH_NOTION_CLIENT_ID=""
-AURA_AUTH_NOTION_CLIENT_SECRET=""
+AURA_AUTH_NOTION_CLIENT_ID=
+AURA_AUTH_NOTION_CLIENT_SECRET=
# TikTok OAuth App Credentials
-AURA_AUTH_TIKTOK_CLIENT_ID=""
-AURA_AUTH_TIKTOK_CLIENT_SECRET=""
+AURA_AUTH_TIKTOK_CLIENT_ID=
+AURA_AUTH_TIKTOK_CLIENT_SECRET=
diff --git a/apps/cloudflare/.env.example b/apps/cloudflare/.env.example
index fce5ea0b..e4bc6cfe 100644
--- a/apps/cloudflare/.env.example
+++ b/apps/cloudflare/.env.example
@@ -1,11 +1,39 @@
-# Salt for key derivation (e.g., password hashing)
-# openssl rand -base64 32
-AURA_AUTH_SALT=
-
-# Opaque secret used to sign and verify JWT tokens
-# openssl rand -base64 32
-AURA_AUTH_SECRET=
-
+# Opaque salting and peppering secrets for key derivation and hashing
+# For example: openssl rand -base64 32
+AURA_AUTH_SALT=""
+
+# Opaque secret for signing JWT tokens and encrypting data
+# For example: openssl rand -base64 32
+AURA_AUTH_SECRET=""
+
+# The base URL of the authentication server
+# For example: http://localhost:4000
+AURA_AUTH_BASE_URL=
+
+# Trusted Origins for CORS and redirect URI validation (comma-separated)
+# For example: http://localhost:3000,http://localhost:4000
+AURA_AUTH_TRUSTED_ORIGINS=
+
+# Enable debug logging for authentication operations (set to "true", "debug", "on", "yes" or "1" to enable)
+AURA_AUTH_DEBUG=
+
+# Log level for authentication operations (e.g. "error", "warn", "info", "debug")
+AURA_AUTH_LOG_LEVEL=
+
+# PEM-encoded RSA public/private key for signing and encrypting JWT tokens in "sealed" JWT mode (JWT with JWS and JWE)
+# For example: -----BEGIN PUBLIC KEY -----\n...\n-----END PUBLIC KEY-----
+AURA_AUTH_SIGNING_PUBLIC_KEY=
+AURA_AUTH_SIGNING_PRIVATE_KEY=
+AURA_AUTH_SIGNING_ALGORITHM=
+
+AURA_AUTH_ENCRYPTION_PUBLIC_KEY=
+AURA_AUTH_ENCRYPTION_PRIVATE_KEY=
+AURA_AUTH_ENCRYPTION_ALGORITHM=
+
+# PEM-encode RSA public/private key either in "signed" or "encrypted" JWT mode (JWT with JWS or JWE)
+AURA_AUTH_PUBLIC_KEY=
+AURA_AUTH_PRIVATE_KEY=
+AURA_AUTH_ALGORITHM=
# Github OAuth App Credentials
AURA_AUTH_GITHUB_CLIENT_ID=
AURA_AUTH_GITHUB_CLIENT_SECRET=
diff --git a/apps/deno/.env.example b/apps/deno/.env.example
index fce5ea0b..e4bc6cfe 100644
--- a/apps/deno/.env.example
+++ b/apps/deno/.env.example
@@ -1,11 +1,39 @@
-# Salt for key derivation (e.g., password hashing)
-# openssl rand -base64 32
-AURA_AUTH_SALT=
-
-# Opaque secret used to sign and verify JWT tokens
-# openssl rand -base64 32
-AURA_AUTH_SECRET=
-
+# Opaque salting and peppering secrets for key derivation and hashing
+# For example: openssl rand -base64 32
+AURA_AUTH_SALT=""
+
+# Opaque secret for signing JWT tokens and encrypting data
+# For example: openssl rand -base64 32
+AURA_AUTH_SECRET=""
+
+# The base URL of the authentication server
+# For example: http://localhost:4000
+AURA_AUTH_BASE_URL=
+
+# Trusted Origins for CORS and redirect URI validation (comma-separated)
+# For example: http://localhost:3000,http://localhost:4000
+AURA_AUTH_TRUSTED_ORIGINS=
+
+# Enable debug logging for authentication operations (set to "true", "debug", "on", "yes" or "1" to enable)
+AURA_AUTH_DEBUG=
+
+# Log level for authentication operations (e.g. "error", "warn", "info", "debug")
+AURA_AUTH_LOG_LEVEL=
+
+# PEM-encoded RSA public/private key for signing and encrypting JWT tokens in "sealed" JWT mode (JWT with JWS and JWE)
+# For example: -----BEGIN PUBLIC KEY -----\n...\n-----END PUBLIC KEY-----
+AURA_AUTH_SIGNING_PUBLIC_KEY=
+AURA_AUTH_SIGNING_PRIVATE_KEY=
+AURA_AUTH_SIGNING_ALGORITHM=
+
+AURA_AUTH_ENCRYPTION_PUBLIC_KEY=
+AURA_AUTH_ENCRYPTION_PRIVATE_KEY=
+AURA_AUTH_ENCRYPTION_ALGORITHM=
+
+# PEM-encode RSA public/private key either in "signed" or "encrypted" JWT mode (JWT with JWS or JWE)
+AURA_AUTH_PUBLIC_KEY=
+AURA_AUTH_PRIVATE_KEY=
+AURA_AUTH_ALGORITHM=
# Github OAuth App Credentials
AURA_AUTH_GITHUB_CLIENT_ID=
AURA_AUTH_GITHUB_CLIENT_SECRET=
diff --git a/apps/elysia/.env.example b/apps/elysia/.env.example
index f5f67e6f..e4bc6cfe 100644
--- a/apps/elysia/.env.example
+++ b/apps/elysia/.env.example
@@ -1,67 +1,95 @@
-# Salt for key derivation (e.g., password hashing)
-# openssl rand -base64 32
+# Opaque salting and peppering secrets for key derivation and hashing
+# For example: openssl rand -base64 32
AURA_AUTH_SALT=""
-# Opaque secret used to sign and verify JWT tokens
-# openssl rand -base64 32
+# Opaque secret for signing JWT tokens and encrypting data
+# For example: openssl rand -base64 32
AURA_AUTH_SECRET=""
+# The base URL of the authentication server
+# For example: http://localhost:4000
+AURA_AUTH_BASE_URL=
+
+# Trusted Origins for CORS and redirect URI validation (comma-separated)
+# For example: http://localhost:3000,http://localhost:4000
+AURA_AUTH_TRUSTED_ORIGINS=
+
+# Enable debug logging for authentication operations (set to "true", "debug", "on", "yes" or "1" to enable)
+AURA_AUTH_DEBUG=
+
+# Log level for authentication operations (e.g. "error", "warn", "info", "debug")
+AURA_AUTH_LOG_LEVEL=
+
+# PEM-encoded RSA public/private key for signing and encrypting JWT tokens in "sealed" JWT mode (JWT with JWS and JWE)
+# For example: -----BEGIN PUBLIC KEY -----\n...\n-----END PUBLIC KEY-----
+AURA_AUTH_SIGNING_PUBLIC_KEY=
+AURA_AUTH_SIGNING_PRIVATE_KEY=
+AURA_AUTH_SIGNING_ALGORITHM=
+
+AURA_AUTH_ENCRYPTION_PUBLIC_KEY=
+AURA_AUTH_ENCRYPTION_PRIVATE_KEY=
+AURA_AUTH_ENCRYPTION_ALGORITHM=
+
+# PEM-encode RSA public/private key either in "signed" or "encrypted" JWT mode (JWT with JWS or JWE)
+AURA_AUTH_PUBLIC_KEY=
+AURA_AUTH_PRIVATE_KEY=
+AURA_AUTH_ALGORITHM=
# Github OAuth App Credentials
-AURA_AUTH_GITHUB_CLIENT_ID=""
-AURA_AUTH_GITHUB_CLIENT_SECRET=""
+AURA_AUTH_GITHUB_CLIENT_ID=
+AURA_AUTH_GITHUB_CLIENT_SECRET=
# Bitbucket OAuth App Credentials
-AURA_AUTH_BITBUCKET_CLIENT_ID=""
-AURA_AUTH_BITBUCKET_CLIENT_SECRET=""
+AURA_AUTH_BITBUCKET_CLIENT_ID=
+AURA_AUTH_BITBUCKET_CLIENT_SECRET=
# Figma OAuth App Credentials
-AURA_AUTH_FIGMA_CLIENT_ID=""
-AURA_AUTH_FIGMA_CLIENT_SECRET=""
+AURA_AUTH_FIGMA_CLIENT_ID=
+AURA_AUTH_FIGMA_CLIENT_SECRET=
# Discord OAuth App Credentials
-AURA_AUTH_DISCORD_CLIENT_ID=""
-AURA_AUTH_DISCORD_CLIENT_SECRET=""
+AURA_AUTH_DISCORD_CLIENT_ID=
+AURA_AUTH_DISCORD_CLIENT_SECRET=
# GitLab OAuth App Credentials
-AURA_AUTH_GITLAB_CLIENT_ID=""
-AURA_AUTH_GITLAB_CLIENT_SECRET=""
+AURA_AUTH_GITLAB_CLIENT_ID=
+AURA_AUTH_GITLAB_CLIENT_SECRET=
# Spotify OAuth App Credentials
-AURA_AUTH_SPOTIFY_CLIENT_ID=""
-AURA_AUTH_SPOTIFY_CLIENT_SECRET=""
+AURA_AUTH_SPOTIFY_CLIENT_ID=
+AURA_AUTH_SPOTIFY_CLIENT_SECRET=
# X (Twitter) OAuth App Credentials
-AURA_AUTH_X_CLIENT_ID=""
-AURA_AUTH_X_CLIENT_SECRET=""
+AURA_AUTH_X_CLIENT_ID=
+AURA_AUTH_X_CLIENT_SECRET=
# Strava OAuth App Credentials
-AURA_AUTH_STRAVA_CLIENT_ID=""
-AURA_AUTH_STRAVA_CLIENT_SECRET=""
+AURA_AUTH_STRAVA_CLIENT_ID=
+AURA_AUTH_STRAVA_CLIENT_SECRET=
# Atlassian OAuth App Credentials
-AURA_AUTH_ATLASSIAN_CLIENT_ID=""
-AURA_AUTH_ATLASSIAN_CLIENT_SECRET=""
+AURA_AUTH_ATLASSIAN_CLIENT_ID=
+AURA_AUTH_ATLASSIAN_CLIENT_SECRET=
# Mailchimp OAuth App Credentials
-AURA_AUTH_MAILCHIMP_CLIENT_ID=""
-AURA_AUTH_MAILCHIMP_CLIENT_SECRET=""
+AURA_AUTH_MAILCHIMP_CLIENT_ID=
+AURA_AUTH_MAILCHIMP_CLIENT_SECRET=
# Pinterest OAuth App Credentials
-AURA_AUTH_PINTEREST_CLIENT_ID=""
-AURA_AUTH_PINTEREST_CLIENT_SECRET=""
+AURA_AUTH_PINTEREST_CLIENT_ID=
+AURA_AUTH_PINTEREST_CLIENT_SECRET=
# Dropbox OAuth App Credentials
-AURA_AUTH_DROPBOX_CLIENT_ID=""
-AURA_AUTH_DROPBOX_CLIENT_SECRET=""
+AURA_AUTH_DROPBOX_CLIENT_ID=
+AURA_AUTH_DROPBOX_CLIENT_SECRET=
# Twitch OAuth App Credentials
-AURA_AUTH_TWITCH_CLIENT_ID=""
-AURA_AUTH_TWITCH_CLIENT_SECRET=""
+AURA_AUTH_TWITCH_CLIENT_ID=
+AURA_AUTH_TWITCH_CLIENT_SECRET=
# Notion OAuth App Credentials
-AURA_AUTH_NOTION_CLIENT_ID=""
-AURA_AUTH_NOTION_CLIENT_SECRET=""
+AURA_AUTH_NOTION_CLIENT_ID=
+AURA_AUTH_NOTION_CLIENT_SECRET=
# TikTok OAuth App Credentials
-AURA_AUTH_TIKTOK_CLIENT_ID=""
-AURA_AUTH_TIKTOK_CLIENT_SECRET=""
+AURA_AUTH_TIKTOK_CLIENT_ID=
+AURA_AUTH_TIKTOK_CLIENT_SECRET=
diff --git a/apps/express/.env.example b/apps/express/.env.example
index f0bb6ccf..e4bc6cfe 100644
--- a/apps/express/.env.example
+++ b/apps/express/.env.example
@@ -1,66 +1,95 @@
-# Opaque secret used to sign and verify JWT tokens
-
-# openssl rand -base64 32
+# Opaque salting and peppering secrets for key derivation and hashing
+# For example: openssl rand -base64 32
AURA_AUTH_SALT=""
-# openssl rand -base64 32
+
+# Opaque secret for signing JWT tokens and encrypting data
+# For example: openssl rand -base64 32
AURA_AUTH_SECRET=""
+# The base URL of the authentication server
+# For example: http://localhost:4000
+AURA_AUTH_BASE_URL=
+
+# Trusted Origins for CORS and redirect URI validation (comma-separated)
+# For example: http://localhost:3000,http://localhost:4000
+AURA_AUTH_TRUSTED_ORIGINS=
+
+# Enable debug logging for authentication operations (set to "true", "debug", "on", "yes" or "1" to enable)
+AURA_AUTH_DEBUG=
+
+# Log level for authentication operations (e.g. "error", "warn", "info", "debug")
+AURA_AUTH_LOG_LEVEL=
+
+# PEM-encoded RSA public/private key for signing and encrypting JWT tokens in "sealed" JWT mode (JWT with JWS and JWE)
+# For example: -----BEGIN PUBLIC KEY -----\n...\n-----END PUBLIC KEY-----
+AURA_AUTH_SIGNING_PUBLIC_KEY=
+AURA_AUTH_SIGNING_PRIVATE_KEY=
+AURA_AUTH_SIGNING_ALGORITHM=
+
+AURA_AUTH_ENCRYPTION_PUBLIC_KEY=
+AURA_AUTH_ENCRYPTION_PRIVATE_KEY=
+AURA_AUTH_ENCRYPTION_ALGORITHM=
+
+# PEM-encode RSA public/private key either in "signed" or "encrypted" JWT mode (JWT with JWS or JWE)
+AURA_AUTH_PUBLIC_KEY=
+AURA_AUTH_PRIVATE_KEY=
+AURA_AUTH_ALGORITHM=
# Github OAuth App Credentials
-AURA_AUTH_GITHUB_CLIENT_ID=""
-AURA_AUTH_GITHUB_CLIENT_SECRET=""
+AURA_AUTH_GITHUB_CLIENT_ID=
+AURA_AUTH_GITHUB_CLIENT_SECRET=
# Bitbucket OAuth App Credentials
-AURA_AUTH_BITBUCKET_CLIENT_ID=""
-AURA_AUTH_BITBUCKET_CLIENT_SECRET=""
+AURA_AUTH_BITBUCKET_CLIENT_ID=
+AURA_AUTH_BITBUCKET_CLIENT_SECRET=
# Figma OAuth App Credentials
-AURA_AUTH_FIGMA_CLIENT_ID=""
-AURA_AUTH_FIGMA_CLIENT_SECRET=""
+AURA_AUTH_FIGMA_CLIENT_ID=
+AURA_AUTH_FIGMA_CLIENT_SECRET=
# Discord OAuth App Credentials
-AURA_AUTH_DISCORD_CLIENT_ID=""
-AURA_AUTH_DISCORD_CLIENT_SECRET=""
+AURA_AUTH_DISCORD_CLIENT_ID=
+AURA_AUTH_DISCORD_CLIENT_SECRET=
# GitLab OAuth App Credentials
-AURA_AUTH_GITLAB_CLIENT_ID=""
-AURA_AUTH_GITLAB_CLIENT_SECRET=""
+AURA_AUTH_GITLAB_CLIENT_ID=
+AURA_AUTH_GITLAB_CLIENT_SECRET=
# Spotify OAuth App Credentials
-AURA_AUTH_SPOTIFY_CLIENT_ID=""
-AURA_AUTH_SPOTIFY_CLIENT_SECRET=""
+AURA_AUTH_SPOTIFY_CLIENT_ID=
+AURA_AUTH_SPOTIFY_CLIENT_SECRET=
# X (Twitter) OAuth App Credentials
-AURA_AUTH_X_CLIENT_ID=""
-AURA_AUTH_X_CLIENT_SECRET=""
+AURA_AUTH_X_CLIENT_ID=
+AURA_AUTH_X_CLIENT_SECRET=
# Strava OAuth App Credentials
-AURA_AUTH_STRAVA_CLIENT_ID=""
-AURA_AUTH_STRAVA_CLIENT_SECRET=""
+AURA_AUTH_STRAVA_CLIENT_ID=
+AURA_AUTH_STRAVA_CLIENT_SECRET=
# Atlassian OAuth App Credentials
-AURA_AUTH_ATLASSIAN_CLIENT_ID=""
-AURA_AUTH_ATLASSIAN_CLIENT_SECRET=""
+AURA_AUTH_ATLASSIAN_CLIENT_ID=
+AURA_AUTH_ATLASSIAN_CLIENT_SECRET=
# Mailchimp OAuth App Credentials
-AURA_AUTH_MAILCHIMP_CLIENT_ID=""
-AURA_AUTH_MAILCHIMP_CLIENT_SECRET=""
+AURA_AUTH_MAILCHIMP_CLIENT_ID=
+AURA_AUTH_MAILCHIMP_CLIENT_SECRET=
# Pinterest OAuth App Credentials
-AURA_AUTH_PINTEREST_CLIENT_ID=""
-AURA_AUTH_PINTEREST_CLIENT_SECRET=""
+AURA_AUTH_PINTEREST_CLIENT_ID=
+AURA_AUTH_PINTEREST_CLIENT_SECRET=
# Dropbox OAuth App Credentials
-AURA_AUTH_DROPBOX_CLIENT_ID=""
-AURA_AUTH_DROPBOX_CLIENT_SECRET=""
+AURA_AUTH_DROPBOX_CLIENT_ID=
+AURA_AUTH_DROPBOX_CLIENT_SECRET=
# Twitch OAuth App Credentials
-AURA_AUTH_TWITCH_CLIENT_ID=""
-AURA_AUTH_TWITCH_CLIENT_SECRET=""
+AURA_AUTH_TWITCH_CLIENT_ID=
+AURA_AUTH_TWITCH_CLIENT_SECRET=
# Notion OAuth App Credentials
-AURA_AUTH_NOTION_CLIENT_ID=""
-AURA_AUTH_NOTION_CLIENT_SECRET=""
+AURA_AUTH_NOTION_CLIENT_ID=
+AURA_AUTH_NOTION_CLIENT_SECRET=
# TikTok OAuth App Credentials
-AURA_AUTH_TIKTOK_CLIENT_ID=""
-AURA_AUTH_TIKTOK_CLIENT_SECRET=""
+AURA_AUTH_TIKTOK_CLIENT_ID=
+AURA_AUTH_TIKTOK_CLIENT_SECRET=
diff --git a/apps/hono/.env.example b/apps/hono/.env.example
index f5f67e6f..e4bc6cfe 100644
--- a/apps/hono/.env.example
+++ b/apps/hono/.env.example
@@ -1,67 +1,95 @@
-# Salt for key derivation (e.g., password hashing)
-# openssl rand -base64 32
+# Opaque salting and peppering secrets for key derivation and hashing
+# For example: openssl rand -base64 32
AURA_AUTH_SALT=""
-# Opaque secret used to sign and verify JWT tokens
-# openssl rand -base64 32
+# Opaque secret for signing JWT tokens and encrypting data
+# For example: openssl rand -base64 32
AURA_AUTH_SECRET=""
+# The base URL of the authentication server
+# For example: http://localhost:4000
+AURA_AUTH_BASE_URL=
+
+# Trusted Origins for CORS and redirect URI validation (comma-separated)
+# For example: http://localhost:3000,http://localhost:4000
+AURA_AUTH_TRUSTED_ORIGINS=
+
+# Enable debug logging for authentication operations (set to "true", "debug", "on", "yes" or "1" to enable)
+AURA_AUTH_DEBUG=
+
+# Log level for authentication operations (e.g. "error", "warn", "info", "debug")
+AURA_AUTH_LOG_LEVEL=
+
+# PEM-encoded RSA public/private key for signing and encrypting JWT tokens in "sealed" JWT mode (JWT with JWS and JWE)
+# For example: -----BEGIN PUBLIC KEY -----\n...\n-----END PUBLIC KEY-----
+AURA_AUTH_SIGNING_PUBLIC_KEY=
+AURA_AUTH_SIGNING_PRIVATE_KEY=
+AURA_AUTH_SIGNING_ALGORITHM=
+
+AURA_AUTH_ENCRYPTION_PUBLIC_KEY=
+AURA_AUTH_ENCRYPTION_PRIVATE_KEY=
+AURA_AUTH_ENCRYPTION_ALGORITHM=
+
+# PEM-encode RSA public/private key either in "signed" or "encrypted" JWT mode (JWT with JWS or JWE)
+AURA_AUTH_PUBLIC_KEY=
+AURA_AUTH_PRIVATE_KEY=
+AURA_AUTH_ALGORITHM=
# Github OAuth App Credentials
-AURA_AUTH_GITHUB_CLIENT_ID=""
-AURA_AUTH_GITHUB_CLIENT_SECRET=""
+AURA_AUTH_GITHUB_CLIENT_ID=
+AURA_AUTH_GITHUB_CLIENT_SECRET=
# Bitbucket OAuth App Credentials
-AURA_AUTH_BITBUCKET_CLIENT_ID=""
-AURA_AUTH_BITBUCKET_CLIENT_SECRET=""
+AURA_AUTH_BITBUCKET_CLIENT_ID=
+AURA_AUTH_BITBUCKET_CLIENT_SECRET=
# Figma OAuth App Credentials
-AURA_AUTH_FIGMA_CLIENT_ID=""
-AURA_AUTH_FIGMA_CLIENT_SECRET=""
+AURA_AUTH_FIGMA_CLIENT_ID=
+AURA_AUTH_FIGMA_CLIENT_SECRET=
# Discord OAuth App Credentials
-AURA_AUTH_DISCORD_CLIENT_ID=""
-AURA_AUTH_DISCORD_CLIENT_SECRET=""
+AURA_AUTH_DISCORD_CLIENT_ID=
+AURA_AUTH_DISCORD_CLIENT_SECRET=
# GitLab OAuth App Credentials
-AURA_AUTH_GITLAB_CLIENT_ID=""
-AURA_AUTH_GITLAB_CLIENT_SECRET=""
+AURA_AUTH_GITLAB_CLIENT_ID=
+AURA_AUTH_GITLAB_CLIENT_SECRET=
# Spotify OAuth App Credentials
-AURA_AUTH_SPOTIFY_CLIENT_ID=""
-AURA_AUTH_SPOTIFY_CLIENT_SECRET=""
+AURA_AUTH_SPOTIFY_CLIENT_ID=
+AURA_AUTH_SPOTIFY_CLIENT_SECRET=
# X (Twitter) OAuth App Credentials
-AURA_AUTH_X_CLIENT_ID=""
-AURA_AUTH_X_CLIENT_SECRET=""
+AURA_AUTH_X_CLIENT_ID=
+AURA_AUTH_X_CLIENT_SECRET=
# Strava OAuth App Credentials
-AURA_AUTH_STRAVA_CLIENT_ID=""
-AURA_AUTH_STRAVA_CLIENT_SECRET=""
+AURA_AUTH_STRAVA_CLIENT_ID=
+AURA_AUTH_STRAVA_CLIENT_SECRET=
# Atlassian OAuth App Credentials
-AURA_AUTH_ATLASSIAN_CLIENT_ID=""
-AURA_AUTH_ATLASSIAN_CLIENT_SECRET=""
+AURA_AUTH_ATLASSIAN_CLIENT_ID=
+AURA_AUTH_ATLASSIAN_CLIENT_SECRET=
# Mailchimp OAuth App Credentials
-AURA_AUTH_MAILCHIMP_CLIENT_ID=""
-AURA_AUTH_MAILCHIMP_CLIENT_SECRET=""
+AURA_AUTH_MAILCHIMP_CLIENT_ID=
+AURA_AUTH_MAILCHIMP_CLIENT_SECRET=
# Pinterest OAuth App Credentials
-AURA_AUTH_PINTEREST_CLIENT_ID=""
-AURA_AUTH_PINTEREST_CLIENT_SECRET=""
+AURA_AUTH_PINTEREST_CLIENT_ID=
+AURA_AUTH_PINTEREST_CLIENT_SECRET=
# Dropbox OAuth App Credentials
-AURA_AUTH_DROPBOX_CLIENT_ID=""
-AURA_AUTH_DROPBOX_CLIENT_SECRET=""
+AURA_AUTH_DROPBOX_CLIENT_ID=
+AURA_AUTH_DROPBOX_CLIENT_SECRET=
# Twitch OAuth App Credentials
-AURA_AUTH_TWITCH_CLIENT_ID=""
-AURA_AUTH_TWITCH_CLIENT_SECRET=""
+AURA_AUTH_TWITCH_CLIENT_ID=
+AURA_AUTH_TWITCH_CLIENT_SECRET=
# Notion OAuth App Credentials
-AURA_AUTH_NOTION_CLIENT_ID=""
-AURA_AUTH_NOTION_CLIENT_SECRET=""
+AURA_AUTH_NOTION_CLIENT_ID=
+AURA_AUTH_NOTION_CLIENT_SECRET=
# TikTok OAuth App Credentials
-AURA_AUTH_TIKTOK_CLIENT_ID=""
-AURA_AUTH_TIKTOK_CLIENT_SECRET=""
+AURA_AUTH_TIKTOK_CLIENT_ID=
+AURA_AUTH_TIKTOK_CLIENT_SECRET=
diff --git a/apps/nextjs/app-router/.env.example b/apps/nextjs/app-router/.env.example
index 34d5cb8d..e4bc6cfe 100644
--- a/apps/nextjs/app-router/.env.example
+++ b/apps/nextjs/app-router/.env.example
@@ -1,132 +1,95 @@
-# Opaque secret used to sign and verify JWT tokens
-
-# openssl rand -base64 32
+# Opaque salting and peppering secrets for key derivation and hashing
+# For example: openssl rand -base64 32
AURA_AUTH_SALT=""
-# openssl rand -base64 32
-AURA_AUTH_SECRET=""
-
-# Github OAuth App Credentials
-AURA_AUTH_GITHUB_CLIENT_ID=""
-AURA_AUTH_GITHUB_CLIENT_SECRET=""
-
-# Bitbucket OAuth App Credentials
-AURA_AUTH_BITBUCKET_CLIENT_ID=""
-AURA_AUTH_BITBUCKET_CLIENT_SECRET=""
-
-# Figma OAuth App Credentials
-AURA_AUTH_FIGMA_CLIENT_ID=""
-AURA_AUTH_FIGMA_CLIENT_SECRET=""
-# Discord OAuth App Credentials
-AURA_AUTH_DISCORD_CLIENT_ID=""
-AURA_AUTH_DISCORD_CLIENT_SECRET=""
-
-# GitLab OAuth App Credentials
-AURA_AUTH_GITLAB_CLIENT_ID=""
-AURA_AUTH_GITLAB_CLIENT_SECRET=""
-
-# Spotify OAuth App Credentials
-AURA_AUTH_SPOTIFY_CLIENT_ID=""
-AURA_AUTH_SPOTIFY_CLIENT_SECRET=""
-
-# X (Twitter) OAuth App Credentials
-AURA_AUTH_X_CLIENT_ID=""
-AURA_AUTH_X_CLIENT_SECRET=""
-
-# Strava OAuth App Credentials
-AURA_AUTH_STRAVA_CLIENT_ID=""
-AURA_AUTH_STRAVA_CLIENT_SECRET=""
-
-# Atlassian OAuth App Credentials
-AURA_AUTH_ATLASSIAN_CLIENT_ID=""
-AURA_AUTH_ATLASSIAN_CLIENT_SECRET=""
-
-# Mailchimp OAuth App Credentials
-AURA_AUTH_MAILCHIMP_CLIENT_ID=""
-AURA_AUTH_MAILCHIMP_CLIENT_SECRET=""
+# Opaque secret for signing JWT tokens and encrypting data
+# For example: openssl rand -base64 32
+AURA_AUTH_SECRET=""
-# Pinterest OAuth App Credentials
-AURA_AUTH_PINTEREST_CLIENT_ID=""
-AURA_AUTH_PINTEREST_CLIENT_SECRET=""
+# The base URL of the authentication server
+# For example: http://localhost:4000
+AURA_AUTH_BASE_URL=
-# Dropbox OAuth App Credentials
-AURA_AUTH_DROPBOX_CLIENT_ID=""
-AURA_AUTH_DROPBOX_CLIENT_SECRET=""
+# Trusted Origins for CORS and redirect URI validation (comma-separated)
+# For example: http://localhost:3000,http://localhost:4000
+AURA_AUTH_TRUSTED_ORIGINS=
-# Twitch OAuth App Credentials
-AURA_AUTH_TWITCH_CLIENT_ID=""
-AURA_AUTH_TWITCH_CLIENT_SECRET=""
+# Enable debug logging for authentication operations (set to "true", "debug", "on", "yes" or "1" to enable)
+AURA_AUTH_DEBUG=
-# Notion OAuth App Credentials
-AURA_AUTH_NOTION_CLIENT_ID=""
-AURA_AUTH_NOTION_CLIENT_SECRET=""
+# Log level for authentication operations (e.g. "error", "warn", "info", "debug")
+AURA_AUTH_LOG_LEVEL=
-# TikTok OAuth App Credentials
-AURA_AUTH_TIKTOK_CLIENT_ID=""
-AURA_AUTH_TIKTOK_CLIENT_SECRET=""
-# Opaque secret used to sign and verify JWT tokens
+# PEM-encoded RSA public/private key for signing and encrypting JWT tokens in "sealed" JWT mode (JWT with JWS and JWE)
+# For example: -----BEGIN PUBLIC KEY -----\n...\n-----END PUBLIC KEY-----
+AURA_AUTH_SIGNING_PUBLIC_KEY=
+AURA_AUTH_SIGNING_PRIVATE_KEY=
+AURA_AUTH_SIGNING_ALGORITHM=
-# openssl rand -base64 32
-AURA_AUTH_SALT=""
-# openssl rand -base64 32
-AURA_AUTH_SECRET=""
+AURA_AUTH_ENCRYPTION_PUBLIC_KEY=
+AURA_AUTH_ENCRYPTION_PRIVATE_KEY=
+AURA_AUTH_ENCRYPTION_ALGORITHM=
+# PEM-encode RSA public/private key either in "signed" or "encrypted" JWT mode (JWT with JWS or JWE)
+AURA_AUTH_PUBLIC_KEY=
+AURA_AUTH_PRIVATE_KEY=
+AURA_AUTH_ALGORITHM=
# Github OAuth App Credentials
-AURA_AUTH_GITHUB_CLIENT_ID=""
-AURA_AUTH_GITHUB_CLIENT_SECRET=""
+AURA_AUTH_GITHUB_CLIENT_ID=
+AURA_AUTH_GITHUB_CLIENT_SECRET=
# Bitbucket OAuth App Credentials
-AURA_AUTH_BITBUCKET_CLIENT_ID=""
-AURA_AUTH_BITBUCKET_CLIENT_SECRET=""
+AURA_AUTH_BITBUCKET_CLIENT_ID=
+AURA_AUTH_BITBUCKET_CLIENT_SECRET=
# Figma OAuth App Credentials
-AURA_AUTH_FIGMA_CLIENT_ID=""
-AURA_AUTH_FIGMA_CLIENT_SECRET=""
+AURA_AUTH_FIGMA_CLIENT_ID=
+AURA_AUTH_FIGMA_CLIENT_SECRET=
# Discord OAuth App Credentials
-AURA_AUTH_DISCORD_CLIENT_ID=""
-AURA_AUTH_DISCORD_CLIENT_SECRET=""
+AURA_AUTH_DISCORD_CLIENT_ID=
+AURA_AUTH_DISCORD_CLIENT_SECRET=
# GitLab OAuth App Credentials
-AURA_AUTH_GITLAB_CLIENT_ID=""
-AURA_AUTH_GITLAB_CLIENT_SECRET=""
+AURA_AUTH_GITLAB_CLIENT_ID=
+AURA_AUTH_GITLAB_CLIENT_SECRET=
# Spotify OAuth App Credentials
-AURA_AUTH_SPOTIFY_CLIENT_ID=""
-AURA_AUTH_SPOTIFY_CLIENT_SECRET=""
+AURA_AUTH_SPOTIFY_CLIENT_ID=
+AURA_AUTH_SPOTIFY_CLIENT_SECRET=
# X (Twitter) OAuth App Credentials
-AURA_AUTH_X_CLIENT_ID=""
-AURA_AUTH_X_CLIENT_SECRET=""
+AURA_AUTH_X_CLIENT_ID=
+AURA_AUTH_X_CLIENT_SECRET=
# Strava OAuth App Credentials
-AURA_AUTH_STRAVA_CLIENT_ID=""
-AURA_AUTH_STRAVA_CLIENT_SECRET=""
+AURA_AUTH_STRAVA_CLIENT_ID=
+AURA_AUTH_STRAVA_CLIENT_SECRET=
# Atlassian OAuth App Credentials
-AURA_AUTH_ATLASSIAN_CLIENT_ID=""
-AURA_AUTH_ATLASSIAN_CLIENT_SECRET=""
+AURA_AUTH_ATLASSIAN_CLIENT_ID=
+AURA_AUTH_ATLASSIAN_CLIENT_SECRET=
# Mailchimp OAuth App Credentials
-AURA_AUTH_MAILCHIMP_CLIENT_ID=""
-AURA_AUTH_MAILCHIMP_CLIENT_SECRET=""
+AURA_AUTH_MAILCHIMP_CLIENT_ID=
+AURA_AUTH_MAILCHIMP_CLIENT_SECRET=
# Pinterest OAuth App Credentials
-AURA_AUTH_PINTEREST_CLIENT_ID=""
-AURA_AUTH_PINTEREST_CLIENT_SECRET=""
+AURA_AUTH_PINTEREST_CLIENT_ID=
+AURA_AUTH_PINTEREST_CLIENT_SECRET=
# Dropbox OAuth App Credentials
-AURA_AUTH_DROPBOX_CLIENT_ID=""
-AURA_AUTH_DROPBOX_CLIENT_SECRET=""
+AURA_AUTH_DROPBOX_CLIENT_ID=
+AURA_AUTH_DROPBOX_CLIENT_SECRET=
# Twitch OAuth App Credentials
-AURA_AUTH_TWITCH_CLIENT_ID=""
-AURA_AUTH_TWITCH_CLIENT_SECRET=""
+AURA_AUTH_TWITCH_CLIENT_ID=
+AURA_AUTH_TWITCH_CLIENT_SECRET=
# Notion OAuth App Credentials
-AURA_AUTH_NOTION_CLIENT_ID=""
-AURA_AUTH_NOTION_CLIENT_SECRET=""
+AURA_AUTH_NOTION_CLIENT_ID=
+AURA_AUTH_NOTION_CLIENT_SECRET=
# TikTok OAuth App Credentials
-AURA_AUTH_TIKTOK_CLIENT_ID=""
-AURA_AUTH_TIKTOK_CLIENT_SECRET=""
+AURA_AUTH_TIKTOK_CLIENT_ID=
+AURA_AUTH_TIKTOK_CLIENT_SECRET=
diff --git a/apps/nextjs/pages-router/.env.example b/apps/nextjs/pages-router/.env.example
index f0bb6ccf..e4bc6cfe 100644
--- a/apps/nextjs/pages-router/.env.example
+++ b/apps/nextjs/pages-router/.env.example
@@ -1,66 +1,95 @@
-# Opaque secret used to sign and verify JWT tokens
-
-# openssl rand -base64 32
+# Opaque salting and peppering secrets for key derivation and hashing
+# For example: openssl rand -base64 32
AURA_AUTH_SALT=""
-# openssl rand -base64 32
+
+# Opaque secret for signing JWT tokens and encrypting data
+# For example: openssl rand -base64 32
AURA_AUTH_SECRET=""
+# The base URL of the authentication server
+# For example: http://localhost:4000
+AURA_AUTH_BASE_URL=
+
+# Trusted Origins for CORS and redirect URI validation (comma-separated)
+# For example: http://localhost:3000,http://localhost:4000
+AURA_AUTH_TRUSTED_ORIGINS=
+
+# Enable debug logging for authentication operations (set to "true", "debug", "on", "yes" or "1" to enable)
+AURA_AUTH_DEBUG=
+
+# Log level for authentication operations (e.g. "error", "warn", "info", "debug")
+AURA_AUTH_LOG_LEVEL=
+
+# PEM-encoded RSA public/private key for signing and encrypting JWT tokens in "sealed" JWT mode (JWT with JWS and JWE)
+# For example: -----BEGIN PUBLIC KEY -----\n...\n-----END PUBLIC KEY-----
+AURA_AUTH_SIGNING_PUBLIC_KEY=
+AURA_AUTH_SIGNING_PRIVATE_KEY=
+AURA_AUTH_SIGNING_ALGORITHM=
+
+AURA_AUTH_ENCRYPTION_PUBLIC_KEY=
+AURA_AUTH_ENCRYPTION_PRIVATE_KEY=
+AURA_AUTH_ENCRYPTION_ALGORITHM=
+
+# PEM-encode RSA public/private key either in "signed" or "encrypted" JWT mode (JWT with JWS or JWE)
+AURA_AUTH_PUBLIC_KEY=
+AURA_AUTH_PRIVATE_KEY=
+AURA_AUTH_ALGORITHM=
# Github OAuth App Credentials
-AURA_AUTH_GITHUB_CLIENT_ID=""
-AURA_AUTH_GITHUB_CLIENT_SECRET=""
+AURA_AUTH_GITHUB_CLIENT_ID=
+AURA_AUTH_GITHUB_CLIENT_SECRET=
# Bitbucket OAuth App Credentials
-AURA_AUTH_BITBUCKET_CLIENT_ID=""
-AURA_AUTH_BITBUCKET_CLIENT_SECRET=""
+AURA_AUTH_BITBUCKET_CLIENT_ID=
+AURA_AUTH_BITBUCKET_CLIENT_SECRET=
# Figma OAuth App Credentials
-AURA_AUTH_FIGMA_CLIENT_ID=""
-AURA_AUTH_FIGMA_CLIENT_SECRET=""
+AURA_AUTH_FIGMA_CLIENT_ID=
+AURA_AUTH_FIGMA_CLIENT_SECRET=
# Discord OAuth App Credentials
-AURA_AUTH_DISCORD_CLIENT_ID=""
-AURA_AUTH_DISCORD_CLIENT_SECRET=""
+AURA_AUTH_DISCORD_CLIENT_ID=
+AURA_AUTH_DISCORD_CLIENT_SECRET=
# GitLab OAuth App Credentials
-AURA_AUTH_GITLAB_CLIENT_ID=""
-AURA_AUTH_GITLAB_CLIENT_SECRET=""
+AURA_AUTH_GITLAB_CLIENT_ID=
+AURA_AUTH_GITLAB_CLIENT_SECRET=
# Spotify OAuth App Credentials
-AURA_AUTH_SPOTIFY_CLIENT_ID=""
-AURA_AUTH_SPOTIFY_CLIENT_SECRET=""
+AURA_AUTH_SPOTIFY_CLIENT_ID=
+AURA_AUTH_SPOTIFY_CLIENT_SECRET=
# X (Twitter) OAuth App Credentials
-AURA_AUTH_X_CLIENT_ID=""
-AURA_AUTH_X_CLIENT_SECRET=""
+AURA_AUTH_X_CLIENT_ID=
+AURA_AUTH_X_CLIENT_SECRET=
# Strava OAuth App Credentials
-AURA_AUTH_STRAVA_CLIENT_ID=""
-AURA_AUTH_STRAVA_CLIENT_SECRET=""
+AURA_AUTH_STRAVA_CLIENT_ID=
+AURA_AUTH_STRAVA_CLIENT_SECRET=
# Atlassian OAuth App Credentials
-AURA_AUTH_ATLASSIAN_CLIENT_ID=""
-AURA_AUTH_ATLASSIAN_CLIENT_SECRET=""
+AURA_AUTH_ATLASSIAN_CLIENT_ID=
+AURA_AUTH_ATLASSIAN_CLIENT_SECRET=
# Mailchimp OAuth App Credentials
-AURA_AUTH_MAILCHIMP_CLIENT_ID=""
-AURA_AUTH_MAILCHIMP_CLIENT_SECRET=""
+AURA_AUTH_MAILCHIMP_CLIENT_ID=
+AURA_AUTH_MAILCHIMP_CLIENT_SECRET=
# Pinterest OAuth App Credentials
-AURA_AUTH_PINTEREST_CLIENT_ID=""
-AURA_AUTH_PINTEREST_CLIENT_SECRET=""
+AURA_AUTH_PINTEREST_CLIENT_ID=
+AURA_AUTH_PINTEREST_CLIENT_SECRET=
# Dropbox OAuth App Credentials
-AURA_AUTH_DROPBOX_CLIENT_ID=""
-AURA_AUTH_DROPBOX_CLIENT_SECRET=""
+AURA_AUTH_DROPBOX_CLIENT_ID=
+AURA_AUTH_DROPBOX_CLIENT_SECRET=
# Twitch OAuth App Credentials
-AURA_AUTH_TWITCH_CLIENT_ID=""
-AURA_AUTH_TWITCH_CLIENT_SECRET=""
+AURA_AUTH_TWITCH_CLIENT_ID=
+AURA_AUTH_TWITCH_CLIENT_SECRET=
# Notion OAuth App Credentials
-AURA_AUTH_NOTION_CLIENT_ID=""
-AURA_AUTH_NOTION_CLIENT_SECRET=""
+AURA_AUTH_NOTION_CLIENT_ID=
+AURA_AUTH_NOTION_CLIENT_SECRET=
# TikTok OAuth App Credentials
-AURA_AUTH_TIKTOK_CLIENT_ID=""
-AURA_AUTH_TIKTOK_CLIENT_SECRET=""
+AURA_AUTH_TIKTOK_CLIENT_ID=
+AURA_AUTH_TIKTOK_CLIENT_SECRET=
diff --git a/apps/nuxt/.env.example b/apps/nuxt/.env.example
index f0bb6ccf..e4bc6cfe 100644
--- a/apps/nuxt/.env.example
+++ b/apps/nuxt/.env.example
@@ -1,66 +1,95 @@
-# Opaque secret used to sign and verify JWT tokens
-
-# openssl rand -base64 32
+# Opaque salting and peppering secrets for key derivation and hashing
+# For example: openssl rand -base64 32
AURA_AUTH_SALT=""
-# openssl rand -base64 32
+
+# Opaque secret for signing JWT tokens and encrypting data
+# For example: openssl rand -base64 32
AURA_AUTH_SECRET=""
+# The base URL of the authentication server
+# For example: http://localhost:4000
+AURA_AUTH_BASE_URL=
+
+# Trusted Origins for CORS and redirect URI validation (comma-separated)
+# For example: http://localhost:3000,http://localhost:4000
+AURA_AUTH_TRUSTED_ORIGINS=
+
+# Enable debug logging for authentication operations (set to "true", "debug", "on", "yes" or "1" to enable)
+AURA_AUTH_DEBUG=
+
+# Log level for authentication operations (e.g. "error", "warn", "info", "debug")
+AURA_AUTH_LOG_LEVEL=
+
+# PEM-encoded RSA public/private key for signing and encrypting JWT tokens in "sealed" JWT mode (JWT with JWS and JWE)
+# For example: -----BEGIN PUBLIC KEY -----\n...\n-----END PUBLIC KEY-----
+AURA_AUTH_SIGNING_PUBLIC_KEY=
+AURA_AUTH_SIGNING_PRIVATE_KEY=
+AURA_AUTH_SIGNING_ALGORITHM=
+
+AURA_AUTH_ENCRYPTION_PUBLIC_KEY=
+AURA_AUTH_ENCRYPTION_PRIVATE_KEY=
+AURA_AUTH_ENCRYPTION_ALGORITHM=
+
+# PEM-encode RSA public/private key either in "signed" or "encrypted" JWT mode (JWT with JWS or JWE)
+AURA_AUTH_PUBLIC_KEY=
+AURA_AUTH_PRIVATE_KEY=
+AURA_AUTH_ALGORITHM=
# Github OAuth App Credentials
-AURA_AUTH_GITHUB_CLIENT_ID=""
-AURA_AUTH_GITHUB_CLIENT_SECRET=""
+AURA_AUTH_GITHUB_CLIENT_ID=
+AURA_AUTH_GITHUB_CLIENT_SECRET=
# Bitbucket OAuth App Credentials
-AURA_AUTH_BITBUCKET_CLIENT_ID=""
-AURA_AUTH_BITBUCKET_CLIENT_SECRET=""
+AURA_AUTH_BITBUCKET_CLIENT_ID=
+AURA_AUTH_BITBUCKET_CLIENT_SECRET=
# Figma OAuth App Credentials
-AURA_AUTH_FIGMA_CLIENT_ID=""
-AURA_AUTH_FIGMA_CLIENT_SECRET=""
+AURA_AUTH_FIGMA_CLIENT_ID=
+AURA_AUTH_FIGMA_CLIENT_SECRET=
# Discord OAuth App Credentials
-AURA_AUTH_DISCORD_CLIENT_ID=""
-AURA_AUTH_DISCORD_CLIENT_SECRET=""
+AURA_AUTH_DISCORD_CLIENT_ID=
+AURA_AUTH_DISCORD_CLIENT_SECRET=
# GitLab OAuth App Credentials
-AURA_AUTH_GITLAB_CLIENT_ID=""
-AURA_AUTH_GITLAB_CLIENT_SECRET=""
+AURA_AUTH_GITLAB_CLIENT_ID=
+AURA_AUTH_GITLAB_CLIENT_SECRET=
# Spotify OAuth App Credentials
-AURA_AUTH_SPOTIFY_CLIENT_ID=""
-AURA_AUTH_SPOTIFY_CLIENT_SECRET=""
+AURA_AUTH_SPOTIFY_CLIENT_ID=
+AURA_AUTH_SPOTIFY_CLIENT_SECRET=
# X (Twitter) OAuth App Credentials
-AURA_AUTH_X_CLIENT_ID=""
-AURA_AUTH_X_CLIENT_SECRET=""
+AURA_AUTH_X_CLIENT_ID=
+AURA_AUTH_X_CLIENT_SECRET=
# Strava OAuth App Credentials
-AURA_AUTH_STRAVA_CLIENT_ID=""
-AURA_AUTH_STRAVA_CLIENT_SECRET=""
+AURA_AUTH_STRAVA_CLIENT_ID=
+AURA_AUTH_STRAVA_CLIENT_SECRET=
# Atlassian OAuth App Credentials
-AURA_AUTH_ATLASSIAN_CLIENT_ID=""
-AURA_AUTH_ATLASSIAN_CLIENT_SECRET=""
+AURA_AUTH_ATLASSIAN_CLIENT_ID=
+AURA_AUTH_ATLASSIAN_CLIENT_SECRET=
# Mailchimp OAuth App Credentials
-AURA_AUTH_MAILCHIMP_CLIENT_ID=""
-AURA_AUTH_MAILCHIMP_CLIENT_SECRET=""
+AURA_AUTH_MAILCHIMP_CLIENT_ID=
+AURA_AUTH_MAILCHIMP_CLIENT_SECRET=
# Pinterest OAuth App Credentials
-AURA_AUTH_PINTEREST_CLIENT_ID=""
-AURA_AUTH_PINTEREST_CLIENT_SECRET=""
+AURA_AUTH_PINTEREST_CLIENT_ID=
+AURA_AUTH_PINTEREST_CLIENT_SECRET=
# Dropbox OAuth App Credentials
-AURA_AUTH_DROPBOX_CLIENT_ID=""
-AURA_AUTH_DROPBOX_CLIENT_SECRET=""
+AURA_AUTH_DROPBOX_CLIENT_ID=
+AURA_AUTH_DROPBOX_CLIENT_SECRET=
# Twitch OAuth App Credentials
-AURA_AUTH_TWITCH_CLIENT_ID=""
-AURA_AUTH_TWITCH_CLIENT_SECRET=""
+AURA_AUTH_TWITCH_CLIENT_ID=
+AURA_AUTH_TWITCH_CLIENT_SECRET=
# Notion OAuth App Credentials
-AURA_AUTH_NOTION_CLIENT_ID=""
-AURA_AUTH_NOTION_CLIENT_SECRET=""
+AURA_AUTH_NOTION_CLIENT_ID=
+AURA_AUTH_NOTION_CLIENT_SECRET=
# TikTok OAuth App Credentials
-AURA_AUTH_TIKTOK_CLIENT_ID=""
-AURA_AUTH_TIKTOK_CLIENT_SECRET=""
+AURA_AUTH_TIKTOK_CLIENT_ID=
+AURA_AUTH_TIKTOK_CLIENT_SECRET=
diff --git a/apps/oak/.env.example b/apps/oak/.env.example
index f5f67e6f..e4bc6cfe 100644
--- a/apps/oak/.env.example
+++ b/apps/oak/.env.example
@@ -1,67 +1,95 @@
-# Salt for key derivation (e.g., password hashing)
-# openssl rand -base64 32
+# Opaque salting and peppering secrets for key derivation and hashing
+# For example: openssl rand -base64 32
AURA_AUTH_SALT=""
-# Opaque secret used to sign and verify JWT tokens
-# openssl rand -base64 32
+# Opaque secret for signing JWT tokens and encrypting data
+# For example: openssl rand -base64 32
AURA_AUTH_SECRET=""
+# The base URL of the authentication server
+# For example: http://localhost:4000
+AURA_AUTH_BASE_URL=
+
+# Trusted Origins for CORS and redirect URI validation (comma-separated)
+# For example: http://localhost:3000,http://localhost:4000
+AURA_AUTH_TRUSTED_ORIGINS=
+
+# Enable debug logging for authentication operations (set to "true", "debug", "on", "yes" or "1" to enable)
+AURA_AUTH_DEBUG=
+
+# Log level for authentication operations (e.g. "error", "warn", "info", "debug")
+AURA_AUTH_LOG_LEVEL=
+
+# PEM-encoded RSA public/private key for signing and encrypting JWT tokens in "sealed" JWT mode (JWT with JWS and JWE)
+# For example: -----BEGIN PUBLIC KEY -----\n...\n-----END PUBLIC KEY-----
+AURA_AUTH_SIGNING_PUBLIC_KEY=
+AURA_AUTH_SIGNING_PRIVATE_KEY=
+AURA_AUTH_SIGNING_ALGORITHM=
+
+AURA_AUTH_ENCRYPTION_PUBLIC_KEY=
+AURA_AUTH_ENCRYPTION_PRIVATE_KEY=
+AURA_AUTH_ENCRYPTION_ALGORITHM=
+
+# PEM-encode RSA public/private key either in "signed" or "encrypted" JWT mode (JWT with JWS or JWE)
+AURA_AUTH_PUBLIC_KEY=
+AURA_AUTH_PRIVATE_KEY=
+AURA_AUTH_ALGORITHM=
# Github OAuth App Credentials
-AURA_AUTH_GITHUB_CLIENT_ID=""
-AURA_AUTH_GITHUB_CLIENT_SECRET=""
+AURA_AUTH_GITHUB_CLIENT_ID=
+AURA_AUTH_GITHUB_CLIENT_SECRET=
# Bitbucket OAuth App Credentials
-AURA_AUTH_BITBUCKET_CLIENT_ID=""
-AURA_AUTH_BITBUCKET_CLIENT_SECRET=""
+AURA_AUTH_BITBUCKET_CLIENT_ID=
+AURA_AUTH_BITBUCKET_CLIENT_SECRET=
# Figma OAuth App Credentials
-AURA_AUTH_FIGMA_CLIENT_ID=""
-AURA_AUTH_FIGMA_CLIENT_SECRET=""
+AURA_AUTH_FIGMA_CLIENT_ID=
+AURA_AUTH_FIGMA_CLIENT_SECRET=
# Discord OAuth App Credentials
-AURA_AUTH_DISCORD_CLIENT_ID=""
-AURA_AUTH_DISCORD_CLIENT_SECRET=""
+AURA_AUTH_DISCORD_CLIENT_ID=
+AURA_AUTH_DISCORD_CLIENT_SECRET=
# GitLab OAuth App Credentials
-AURA_AUTH_GITLAB_CLIENT_ID=""
-AURA_AUTH_GITLAB_CLIENT_SECRET=""
+AURA_AUTH_GITLAB_CLIENT_ID=
+AURA_AUTH_GITLAB_CLIENT_SECRET=
# Spotify OAuth App Credentials
-AURA_AUTH_SPOTIFY_CLIENT_ID=""
-AURA_AUTH_SPOTIFY_CLIENT_SECRET=""
+AURA_AUTH_SPOTIFY_CLIENT_ID=
+AURA_AUTH_SPOTIFY_CLIENT_SECRET=
# X (Twitter) OAuth App Credentials
-AURA_AUTH_X_CLIENT_ID=""
-AURA_AUTH_X_CLIENT_SECRET=""
+AURA_AUTH_X_CLIENT_ID=
+AURA_AUTH_X_CLIENT_SECRET=
# Strava OAuth App Credentials
-AURA_AUTH_STRAVA_CLIENT_ID=""
-AURA_AUTH_STRAVA_CLIENT_SECRET=""
+AURA_AUTH_STRAVA_CLIENT_ID=
+AURA_AUTH_STRAVA_CLIENT_SECRET=
# Atlassian OAuth App Credentials
-AURA_AUTH_ATLASSIAN_CLIENT_ID=""
-AURA_AUTH_ATLASSIAN_CLIENT_SECRET=""
+AURA_AUTH_ATLASSIAN_CLIENT_ID=
+AURA_AUTH_ATLASSIAN_CLIENT_SECRET=
# Mailchimp OAuth App Credentials
-AURA_AUTH_MAILCHIMP_CLIENT_ID=""
-AURA_AUTH_MAILCHIMP_CLIENT_SECRET=""
+AURA_AUTH_MAILCHIMP_CLIENT_ID=
+AURA_AUTH_MAILCHIMP_CLIENT_SECRET=
# Pinterest OAuth App Credentials
-AURA_AUTH_PINTEREST_CLIENT_ID=""
-AURA_AUTH_PINTEREST_CLIENT_SECRET=""
+AURA_AUTH_PINTEREST_CLIENT_ID=
+AURA_AUTH_PINTEREST_CLIENT_SECRET=
# Dropbox OAuth App Credentials
-AURA_AUTH_DROPBOX_CLIENT_ID=""
-AURA_AUTH_DROPBOX_CLIENT_SECRET=""
+AURA_AUTH_DROPBOX_CLIENT_ID=
+AURA_AUTH_DROPBOX_CLIENT_SECRET=
# Twitch OAuth App Credentials
-AURA_AUTH_TWITCH_CLIENT_ID=""
-AURA_AUTH_TWITCH_CLIENT_SECRET=""
+AURA_AUTH_TWITCH_CLIENT_ID=
+AURA_AUTH_TWITCH_CLIENT_SECRET=
# Notion OAuth App Credentials
-AURA_AUTH_NOTION_CLIENT_ID=""
-AURA_AUTH_NOTION_CLIENT_SECRET=""
+AURA_AUTH_NOTION_CLIENT_ID=
+AURA_AUTH_NOTION_CLIENT_SECRET=
# TikTok OAuth App Credentials
-AURA_AUTH_TIKTOK_CLIENT_ID=""
-AURA_AUTH_TIKTOK_CLIENT_SECRET=""
+AURA_AUTH_TIKTOK_CLIENT_ID=
+AURA_AUTH_TIKTOK_CLIENT_SECRET=
diff --git a/apps/react-router/.env.example b/apps/react-router/.env.example
index f0bb6ccf..e4bc6cfe 100644
--- a/apps/react-router/.env.example
+++ b/apps/react-router/.env.example
@@ -1,66 +1,95 @@
-# Opaque secret used to sign and verify JWT tokens
-
-# openssl rand -base64 32
+# Opaque salting and peppering secrets for key derivation and hashing
+# For example: openssl rand -base64 32
AURA_AUTH_SALT=""
-# openssl rand -base64 32
+
+# Opaque secret for signing JWT tokens and encrypting data
+# For example: openssl rand -base64 32
AURA_AUTH_SECRET=""
+# The base URL of the authentication server
+# For example: http://localhost:4000
+AURA_AUTH_BASE_URL=
+
+# Trusted Origins for CORS and redirect URI validation (comma-separated)
+# For example: http://localhost:3000,http://localhost:4000
+AURA_AUTH_TRUSTED_ORIGINS=
+
+# Enable debug logging for authentication operations (set to "true", "debug", "on", "yes" or "1" to enable)
+AURA_AUTH_DEBUG=
+
+# Log level for authentication operations (e.g. "error", "warn", "info", "debug")
+AURA_AUTH_LOG_LEVEL=
+
+# PEM-encoded RSA public/private key for signing and encrypting JWT tokens in "sealed" JWT mode (JWT with JWS and JWE)
+# For example: -----BEGIN PUBLIC KEY -----\n...\n-----END PUBLIC KEY-----
+AURA_AUTH_SIGNING_PUBLIC_KEY=
+AURA_AUTH_SIGNING_PRIVATE_KEY=
+AURA_AUTH_SIGNING_ALGORITHM=
+
+AURA_AUTH_ENCRYPTION_PUBLIC_KEY=
+AURA_AUTH_ENCRYPTION_PRIVATE_KEY=
+AURA_AUTH_ENCRYPTION_ALGORITHM=
+
+# PEM-encode RSA public/private key either in "signed" or "encrypted" JWT mode (JWT with JWS or JWE)
+AURA_AUTH_PUBLIC_KEY=
+AURA_AUTH_PRIVATE_KEY=
+AURA_AUTH_ALGORITHM=
# Github OAuth App Credentials
-AURA_AUTH_GITHUB_CLIENT_ID=""
-AURA_AUTH_GITHUB_CLIENT_SECRET=""
+AURA_AUTH_GITHUB_CLIENT_ID=
+AURA_AUTH_GITHUB_CLIENT_SECRET=
# Bitbucket OAuth App Credentials
-AURA_AUTH_BITBUCKET_CLIENT_ID=""
-AURA_AUTH_BITBUCKET_CLIENT_SECRET=""
+AURA_AUTH_BITBUCKET_CLIENT_ID=
+AURA_AUTH_BITBUCKET_CLIENT_SECRET=
# Figma OAuth App Credentials
-AURA_AUTH_FIGMA_CLIENT_ID=""
-AURA_AUTH_FIGMA_CLIENT_SECRET=""
+AURA_AUTH_FIGMA_CLIENT_ID=
+AURA_AUTH_FIGMA_CLIENT_SECRET=
# Discord OAuth App Credentials
-AURA_AUTH_DISCORD_CLIENT_ID=""
-AURA_AUTH_DISCORD_CLIENT_SECRET=""
+AURA_AUTH_DISCORD_CLIENT_ID=
+AURA_AUTH_DISCORD_CLIENT_SECRET=
# GitLab OAuth App Credentials
-AURA_AUTH_GITLAB_CLIENT_ID=""
-AURA_AUTH_GITLAB_CLIENT_SECRET=""
+AURA_AUTH_GITLAB_CLIENT_ID=
+AURA_AUTH_GITLAB_CLIENT_SECRET=
# Spotify OAuth App Credentials
-AURA_AUTH_SPOTIFY_CLIENT_ID=""
-AURA_AUTH_SPOTIFY_CLIENT_SECRET=""
+AURA_AUTH_SPOTIFY_CLIENT_ID=
+AURA_AUTH_SPOTIFY_CLIENT_SECRET=
# X (Twitter) OAuth App Credentials
-AURA_AUTH_X_CLIENT_ID=""
-AURA_AUTH_X_CLIENT_SECRET=""
+AURA_AUTH_X_CLIENT_ID=
+AURA_AUTH_X_CLIENT_SECRET=
# Strava OAuth App Credentials
-AURA_AUTH_STRAVA_CLIENT_ID=""
-AURA_AUTH_STRAVA_CLIENT_SECRET=""
+AURA_AUTH_STRAVA_CLIENT_ID=
+AURA_AUTH_STRAVA_CLIENT_SECRET=
# Atlassian OAuth App Credentials
-AURA_AUTH_ATLASSIAN_CLIENT_ID=""
-AURA_AUTH_ATLASSIAN_CLIENT_SECRET=""
+AURA_AUTH_ATLASSIAN_CLIENT_ID=
+AURA_AUTH_ATLASSIAN_CLIENT_SECRET=
# Mailchimp OAuth App Credentials
-AURA_AUTH_MAILCHIMP_CLIENT_ID=""
-AURA_AUTH_MAILCHIMP_CLIENT_SECRET=""
+AURA_AUTH_MAILCHIMP_CLIENT_ID=
+AURA_AUTH_MAILCHIMP_CLIENT_SECRET=
# Pinterest OAuth App Credentials
-AURA_AUTH_PINTEREST_CLIENT_ID=""
-AURA_AUTH_PINTEREST_CLIENT_SECRET=""
+AURA_AUTH_PINTEREST_CLIENT_ID=
+AURA_AUTH_PINTEREST_CLIENT_SECRET=
# Dropbox OAuth App Credentials
-AURA_AUTH_DROPBOX_CLIENT_ID=""
-AURA_AUTH_DROPBOX_CLIENT_SECRET=""
+AURA_AUTH_DROPBOX_CLIENT_ID=
+AURA_AUTH_DROPBOX_CLIENT_SECRET=
# Twitch OAuth App Credentials
-AURA_AUTH_TWITCH_CLIENT_ID=""
-AURA_AUTH_TWITCH_CLIENT_SECRET=""
+AURA_AUTH_TWITCH_CLIENT_ID=
+AURA_AUTH_TWITCH_CLIENT_SECRET=
# Notion OAuth App Credentials
-AURA_AUTH_NOTION_CLIENT_ID=""
-AURA_AUTH_NOTION_CLIENT_SECRET=""
+AURA_AUTH_NOTION_CLIENT_ID=
+AURA_AUTH_NOTION_CLIENT_SECRET=
# TikTok OAuth App Credentials
-AURA_AUTH_TIKTOK_CLIENT_ID=""
-AURA_AUTH_TIKTOK_CLIENT_SECRET=""
+AURA_AUTH_TIKTOK_CLIENT_ID=
+AURA_AUTH_TIKTOK_CLIENT_SECRET=
diff --git a/apps/supabase/.env.example b/apps/supabase/.env.example
index fce5ea0b..e4bc6cfe 100644
--- a/apps/supabase/.env.example
+++ b/apps/supabase/.env.example
@@ -1,11 +1,39 @@
-# Salt for key derivation (e.g., password hashing)
-# openssl rand -base64 32
-AURA_AUTH_SALT=
-
-# Opaque secret used to sign and verify JWT tokens
-# openssl rand -base64 32
-AURA_AUTH_SECRET=
-
+# Opaque salting and peppering secrets for key derivation and hashing
+# For example: openssl rand -base64 32
+AURA_AUTH_SALT=""
+
+# Opaque secret for signing JWT tokens and encrypting data
+# For example: openssl rand -base64 32
+AURA_AUTH_SECRET=""
+
+# The base URL of the authentication server
+# For example: http://localhost:4000
+AURA_AUTH_BASE_URL=
+
+# Trusted Origins for CORS and redirect URI validation (comma-separated)
+# For example: http://localhost:3000,http://localhost:4000
+AURA_AUTH_TRUSTED_ORIGINS=
+
+# Enable debug logging for authentication operations (set to "true", "debug", "on", "yes" or "1" to enable)
+AURA_AUTH_DEBUG=
+
+# Log level for authentication operations (e.g. "error", "warn", "info", "debug")
+AURA_AUTH_LOG_LEVEL=
+
+# PEM-encoded RSA public/private key for signing and encrypting JWT tokens in "sealed" JWT mode (JWT with JWS and JWE)
+# For example: -----BEGIN PUBLIC KEY -----\n...\n-----END PUBLIC KEY-----
+AURA_AUTH_SIGNING_PUBLIC_KEY=
+AURA_AUTH_SIGNING_PRIVATE_KEY=
+AURA_AUTH_SIGNING_ALGORITHM=
+
+AURA_AUTH_ENCRYPTION_PUBLIC_KEY=
+AURA_AUTH_ENCRYPTION_PRIVATE_KEY=
+AURA_AUTH_ENCRYPTION_ALGORITHM=
+
+# PEM-encode RSA public/private key either in "signed" or "encrypted" JWT mode (JWT with JWS or JWE)
+AURA_AUTH_PUBLIC_KEY=
+AURA_AUTH_PRIVATE_KEY=
+AURA_AUTH_ALGORITHM=
# Github OAuth App Credentials
AURA_AUTH_GITHUB_CLIENT_ID=
AURA_AUTH_GITHUB_CLIENT_SECRET=
diff --git a/apps/tanstack-start/.env.example b/apps/tanstack-start/.env.example
index f0bb6ccf..e4bc6cfe 100644
--- a/apps/tanstack-start/.env.example
+++ b/apps/tanstack-start/.env.example
@@ -1,66 +1,95 @@
-# Opaque secret used to sign and verify JWT tokens
-
-# openssl rand -base64 32
+# Opaque salting and peppering secrets for key derivation and hashing
+# For example: openssl rand -base64 32
AURA_AUTH_SALT=""
-# openssl rand -base64 32
+
+# Opaque secret for signing JWT tokens and encrypting data
+# For example: openssl rand -base64 32
AURA_AUTH_SECRET=""
+# The base URL of the authentication server
+# For example: http://localhost:4000
+AURA_AUTH_BASE_URL=
+
+# Trusted Origins for CORS and redirect URI validation (comma-separated)
+# For example: http://localhost:3000,http://localhost:4000
+AURA_AUTH_TRUSTED_ORIGINS=
+
+# Enable debug logging for authentication operations (set to "true", "debug", "on", "yes" or "1" to enable)
+AURA_AUTH_DEBUG=
+
+# Log level for authentication operations (e.g. "error", "warn", "info", "debug")
+AURA_AUTH_LOG_LEVEL=
+
+# PEM-encoded RSA public/private key for signing and encrypting JWT tokens in "sealed" JWT mode (JWT with JWS and JWE)
+# For example: -----BEGIN PUBLIC KEY -----\n...\n-----END PUBLIC KEY-----
+AURA_AUTH_SIGNING_PUBLIC_KEY=
+AURA_AUTH_SIGNING_PRIVATE_KEY=
+AURA_AUTH_SIGNING_ALGORITHM=
+
+AURA_AUTH_ENCRYPTION_PUBLIC_KEY=
+AURA_AUTH_ENCRYPTION_PRIVATE_KEY=
+AURA_AUTH_ENCRYPTION_ALGORITHM=
+
+# PEM-encode RSA public/private key either in "signed" or "encrypted" JWT mode (JWT with JWS or JWE)
+AURA_AUTH_PUBLIC_KEY=
+AURA_AUTH_PRIVATE_KEY=
+AURA_AUTH_ALGORITHM=
# Github OAuth App Credentials
-AURA_AUTH_GITHUB_CLIENT_ID=""
-AURA_AUTH_GITHUB_CLIENT_SECRET=""
+AURA_AUTH_GITHUB_CLIENT_ID=
+AURA_AUTH_GITHUB_CLIENT_SECRET=
# Bitbucket OAuth App Credentials
-AURA_AUTH_BITBUCKET_CLIENT_ID=""
-AURA_AUTH_BITBUCKET_CLIENT_SECRET=""
+AURA_AUTH_BITBUCKET_CLIENT_ID=
+AURA_AUTH_BITBUCKET_CLIENT_SECRET=
# Figma OAuth App Credentials
-AURA_AUTH_FIGMA_CLIENT_ID=""
-AURA_AUTH_FIGMA_CLIENT_SECRET=""
+AURA_AUTH_FIGMA_CLIENT_ID=
+AURA_AUTH_FIGMA_CLIENT_SECRET=
# Discord OAuth App Credentials
-AURA_AUTH_DISCORD_CLIENT_ID=""
-AURA_AUTH_DISCORD_CLIENT_SECRET=""
+AURA_AUTH_DISCORD_CLIENT_ID=
+AURA_AUTH_DISCORD_CLIENT_SECRET=
# GitLab OAuth App Credentials
-AURA_AUTH_GITLAB_CLIENT_ID=""
-AURA_AUTH_GITLAB_CLIENT_SECRET=""
+AURA_AUTH_GITLAB_CLIENT_ID=
+AURA_AUTH_GITLAB_CLIENT_SECRET=
# Spotify OAuth App Credentials
-AURA_AUTH_SPOTIFY_CLIENT_ID=""
-AURA_AUTH_SPOTIFY_CLIENT_SECRET=""
+AURA_AUTH_SPOTIFY_CLIENT_ID=
+AURA_AUTH_SPOTIFY_CLIENT_SECRET=
# X (Twitter) OAuth App Credentials
-AURA_AUTH_X_CLIENT_ID=""
-AURA_AUTH_X_CLIENT_SECRET=""
+AURA_AUTH_X_CLIENT_ID=
+AURA_AUTH_X_CLIENT_SECRET=
# Strava OAuth App Credentials
-AURA_AUTH_STRAVA_CLIENT_ID=""
-AURA_AUTH_STRAVA_CLIENT_SECRET=""
+AURA_AUTH_STRAVA_CLIENT_ID=
+AURA_AUTH_STRAVA_CLIENT_SECRET=
# Atlassian OAuth App Credentials
-AURA_AUTH_ATLASSIAN_CLIENT_ID=""
-AURA_AUTH_ATLASSIAN_CLIENT_SECRET=""
+AURA_AUTH_ATLASSIAN_CLIENT_ID=
+AURA_AUTH_ATLASSIAN_CLIENT_SECRET=
# Mailchimp OAuth App Credentials
-AURA_AUTH_MAILCHIMP_CLIENT_ID=""
-AURA_AUTH_MAILCHIMP_CLIENT_SECRET=""
+AURA_AUTH_MAILCHIMP_CLIENT_ID=
+AURA_AUTH_MAILCHIMP_CLIENT_SECRET=
# Pinterest OAuth App Credentials
-AURA_AUTH_PINTEREST_CLIENT_ID=""
-AURA_AUTH_PINTEREST_CLIENT_SECRET=""
+AURA_AUTH_PINTEREST_CLIENT_ID=
+AURA_AUTH_PINTEREST_CLIENT_SECRET=
# Dropbox OAuth App Credentials
-AURA_AUTH_DROPBOX_CLIENT_ID=""
-AURA_AUTH_DROPBOX_CLIENT_SECRET=""
+AURA_AUTH_DROPBOX_CLIENT_ID=
+AURA_AUTH_DROPBOX_CLIENT_SECRET=
# Twitch OAuth App Credentials
-AURA_AUTH_TWITCH_CLIENT_ID=""
-AURA_AUTH_TWITCH_CLIENT_SECRET=""
+AURA_AUTH_TWITCH_CLIENT_ID=
+AURA_AUTH_TWITCH_CLIENT_SECRET=
# Notion OAuth App Credentials
-AURA_AUTH_NOTION_CLIENT_ID=""
-AURA_AUTH_NOTION_CLIENT_SECRET=""
+AURA_AUTH_NOTION_CLIENT_ID=
+AURA_AUTH_NOTION_CLIENT_SECRET=
# TikTok OAuth App Credentials
-AURA_AUTH_TIKTOK_CLIENT_ID=""
-AURA_AUTH_TIKTOK_CLIENT_SECRET=""
+AURA_AUTH_TIKTOK_CLIENT_ID=
+AURA_AUTH_TIKTOK_CLIENT_SECRET=
diff --git a/apps/vercel/.env.example b/apps/vercel/.env.example
index fce5ea0b..e4bc6cfe 100644
--- a/apps/vercel/.env.example
+++ b/apps/vercel/.env.example
@@ -1,11 +1,39 @@
-# Salt for key derivation (e.g., password hashing)
-# openssl rand -base64 32
-AURA_AUTH_SALT=
-
-# Opaque secret used to sign and verify JWT tokens
-# openssl rand -base64 32
-AURA_AUTH_SECRET=
-
+# Opaque salting and peppering secrets for key derivation and hashing
+# For example: openssl rand -base64 32
+AURA_AUTH_SALT=""
+
+# Opaque secret for signing JWT tokens and encrypting data
+# For example: openssl rand -base64 32
+AURA_AUTH_SECRET=""
+
+# The base URL of the authentication server
+# For example: http://localhost:4000
+AURA_AUTH_BASE_URL=
+
+# Trusted Origins for CORS and redirect URI validation (comma-separated)
+# For example: http://localhost:3000,http://localhost:4000
+AURA_AUTH_TRUSTED_ORIGINS=
+
+# Enable debug logging for authentication operations (set to "true", "debug", "on", "yes" or "1" to enable)
+AURA_AUTH_DEBUG=
+
+# Log level for authentication operations (e.g. "error", "warn", "info", "debug")
+AURA_AUTH_LOG_LEVEL=
+
+# PEM-encoded RSA public/private key for signing and encrypting JWT tokens in "sealed" JWT mode (JWT with JWS and JWE)
+# For example: -----BEGIN PUBLIC KEY -----\n...\n-----END PUBLIC KEY-----
+AURA_AUTH_SIGNING_PUBLIC_KEY=
+AURA_AUTH_SIGNING_PRIVATE_KEY=
+AURA_AUTH_SIGNING_ALGORITHM=
+
+AURA_AUTH_ENCRYPTION_PUBLIC_KEY=
+AURA_AUTH_ENCRYPTION_PRIVATE_KEY=
+AURA_AUTH_ENCRYPTION_ALGORITHM=
+
+# PEM-encode RSA public/private key either in "signed" or "encrypted" JWT mode (JWT with JWS or JWE)
+AURA_AUTH_PUBLIC_KEY=
+AURA_AUTH_PRIVATE_KEY=
+AURA_AUTH_ALGORITHM=
# Github OAuth App Credentials
AURA_AUTH_GITHUB_CLIENT_ID=
AURA_AUTH_GITHUB_CLIENT_SECRET=
diff --git a/docs/src/content/docs/configuration/env.mdx b/docs/src/content/docs/configuration/env.mdx
index 570c8633..bab2880a 100644
--- a/docs/src/content/docs/configuration/env.mdx
+++ b/docs/src/content/docs/configuration/env.mdx
@@ -25,6 +25,7 @@ For more details, read:
- [Secure variables](#secure-variables)
- [OAuth variables](#oauth-variables)
- [Runtime variables](#runtime-variables)
+- [PEM variables](#pem-variables)
@@ -111,3 +112,52 @@ AURA_AUTH_DEBUG="1"
# Built-in logger level
AURA_AUTH_LOG_LEVEL="info"
```
+
+### PEM Variables
+
+PEM variables are used to provide PEM-encoded keys and certificates for JWT signing and encryption. They can be set directly as environment variables, or loaded from files using the `_PUBLIC_KEY` and `_PRIVATE_KEY` suffix patterns.
+
+The PEM variables supported by Aura Auth are related to the `jwt.mode` configuration option which defines if the JWT is signed, encrypted or both. When using PEM keys you can also control the algorithms used to import those keys via two configuration fields:
+
+- `jwt.signingAlgorithm` — algorithm used to import PEM-formatted signing keys (e.g. `RS256`, `ES256`).
+- `jwt.keyAlgorithm` — algorithm used to import PEM-formatted encryption keys (e.g. `RSA-OAEP`, `RSA-OAEP-256`).
+
+These can be set either in the `createAuth` session config or via environment variables `SIGNING_ALGORITHM` / `SIGNING_ALG` and `ENCRYPTION_ALGORITHM` / `ENCRYPTION_ALG` respectively. If not provided a sensible default is chosen (`RS256` for signing imports, `RSA-OAEP-256` for encryption imports).
+
+| Pattern | Description |
+| ------------------- | ---------------------------------------------------------------------------------------------------------------------- |
+| `PUBLIC_KEY` | PEM-encoded public key used when `jwt.mode` is `signed` or `encrypted` (single key pair for one operation). |
+| `PRIVATE_KEY` | PEM-encoded private key used when `jwt.mode` is `signed` or `encrypted` (single key pair for one operation). |
+| `{KEY}_PUBLIC_KEY` | PEM-encoded public key for `sealed` JWT mode where `{KEY}` is `SIGNING` or `ENCRYPTION` (e.g. `SIGNING_PUBLIC_KEY`). |
+| `{KEY}_PRIVATE_KEY` | PEM-encoded private key for `sealed` JWT mode where `{KEY}` is `SIGNING` or `ENCRYPTION` (e.g. `SIGNING_PRIVATE_KEY`). |
+
+
+ The loading of PEM variables depends on the `jwt.mode` configuration option. If `jwt.mode` is set to `signed` or `encrypted`,
+ Aura Auth will look for the `PUBLIC_KEY` and `PRIVATE_KEY` environment variables. If `jwt.mode` is set to `sealed`, Aura Auth
+ will look for separate key pairs: `SIGNING_PUBLIC_KEY`/`SIGNING_PRIVATE_KEY` and
+ `ENCRYPTION_PUBLIC_KEY`/`ENCRYPTION_PRIVATE_KEY`. When importing PEM keys Aura Auth needs to know which algorithm to use for the
+ import step. Set `SIGNING_ALGORITHM`/`SIGNING_ALG` to control the signing key import (or use `jwt.signingAlgorithm` in the
+ session config). Set `ENCRYPTION_ALGORITHM`/`ENCRYPTION_ALG` to control the encryption key import (or use `jwt.keyAlgorithm` in
+ the session config). Environment variables take precedence over the session config.
+
+
+#### Loading PEM variables from files
+
+PEM variables for `signed` or `encrypted` JWT modes can be loaded from files using the `PUBLIC_KEY` and `PRIVATE_KEY` suffix patterns. For example, if you have a signing key pair stored in `signing_public.pem` and `signing_private.pem`, you can set the following environment variables:
+
+```bash title=".env"
+AURA_AUTH_PUBLIC_KEY='-----BEGIN PUBLIC KEY----- abcd1234 -----END PUBLIC KEY-----'
+AURA_AUTH_PRIVATE_KEY='-----BEGIN PRIVATE KEY----- abcd1234 -----END PRIVATE KEY-----'
+```
+
+PEM variables for `sealed` JWT mode can also be loaded from files using the `_PUBLIC_KEY` and `_PRIVATE_KEY` variable names. For example, if you have a key pair stored in `public.pem` and `private.pem`, you can set the following environment variables:
+
+```bash title=".env"
+# PEM variables for signing JWTs
+AURA_AUTH_SIGNING_PUBLIC_KEY='-----BEGIN PUBLIC KEY----- abcd1234 -----END PUBLIC KEY-----'
+AURA_AUTH_SIGNING_PRIVATE_KEY='-----BEGIN PRIVATE KEY----- abcd1234 -----END PRIVATE KEY-----'
+
+# PEM variables for encrypting JWTs
+AURA_AUTH_ENCRYPTION_PUBLIC_KEY='-----BEGIN PUBLIC KEY----- abcd1234 -----END PUBLIC KEY-----'
+AURA_AUTH_ENCRYPTION_PRIVATE_KEY='-----BEGIN PRIVATE KEY----- abcd1234 -----END PRIVATE KEY-----'
+```
diff --git a/packages/core/src/@types/session.ts b/packages/core/src/@types/session.ts
index 949a7401..05cb25ba 100644
--- a/packages/core/src/@types/session.ts
+++ b/packages/core/src/@types/session.ts
@@ -1,7 +1,7 @@
import type { infer as Infer } from "zod/v4/core"
import type { TypedJWTPayload } from "@aura-stack/jose"
import type { UserIdentity, UserShape } from "@/shared/identity.ts"
-import type { DeepPartial, EditableShape, ZodShapeToObject } from "@/@types/utility.ts"
+import type { DeepPartial, EditableShape, Prettify, ZodShapeToObject } from "@/@types/utility.ts"
import type { CookieStoreConfig, IdentityConfig, InternalLogger, JoseInstance } from "@/@types/config.ts"
/** Application user type, inferred from the configured identity schema (defaults to the built-in user shape). */
@@ -22,6 +22,11 @@ export interface CryptoSecret {
encrypt: CryptoKey | CryptoKeyPair
}
+export interface AsymmetricKeyPairFromEnv {
+ publicKey: string
+ privateKey: string
+}
+
/**
* A symmetric secret or asymmetric key pair used for JWT operations.
*
@@ -108,32 +113,34 @@ export type JWTConfigBase = JWTSignedMode | JWTEncryptedMode | JWTSealedMode
/** How session/JWT lifetime is enforced relative to `iat`, absolute caps, and sliding windows. */
export type JWTExpirationStrategy = "fixed" | "rolling" | "absolute" | "sliding"
-export type JWTConfig = {
- /**
- * Token lifetime.
- */
- maxAge?: number
- /**
- * JWT `iss` (issuer) claim. Set this to your app's canonical URL.
- * @example "https://auth.example.com"
- */
- issuer?: string
- /**
- * JWT `aud` claim. Single value or array for multi-audience tokens.
- * @example ["https://api.example.com", "https://app.example.com"]
- */
- audience?: string | string[]
- /**
- * Maximum absolute session duration in seconds.
- * Required for "absolute" and "sliding" strategies.
- * Enforced via jose's maxTokenAge against the iat claim.
- */
- maxExpiration?: number
- /**
- * Policy for renewing or capping token lifetime (pairs with `maxExpiration` where applicable).
- */
- expirationStrategy?: JWTExpirationStrategy
-} & JWTConfigBase
+export type JWTConfig = Prettify<
+ {
+ /**
+ * Token lifetime.
+ */
+ maxAge?: number
+ /**
+ * JWT `iss` (issuer) claim. Set this to your app's canonical URL.
+ * @example "https://auth.example.com"
+ */
+ issuer?: string
+ /**
+ * JWT `aud` claim. Single value or array for multi-audience tokens.
+ * @example ["https://api.example.com", "https://app.example.com"]
+ */
+ audience?: string | string[]
+ /**
+ * Maximum absolute session duration in seconds.
+ * Required for "absolute" and "sliding" strategies.
+ * Enforced via jose's maxTokenAge against the iat claim.
+ */
+ maxExpiration?: number
+ /**
+ * Policy for renewing or capping token lifetime (pairs with `maxExpiration` where applicable).
+ */
+ expirationStrategy?: JWTExpirationStrategy
+ } & JWTConfigBase
+>
/**
* Stateless JWT strategy.
diff --git a/packages/core/src/jose.ts b/packages/core/src/jose.ts
index d4d60e49..53bad07d 100644
--- a/packages/core/src/jose.ts
+++ b/packages/core/src/jose.ts
@@ -14,10 +14,20 @@ import {
type JWTDecryptOptions,
} from "@aura-stack/jose"
export { base64url, type JWTPayload } from "@aura-stack/jose/jose"
-import { AuthInternalError, AuthSecurityError } from "@/shared/errors.ts"
-import { isCryptoKey, isCryptoKeyPair, isCryptoSecret, isEncryptedMode, isSealedMode, isSignedMode } from "@/shared/assert.ts"
+import { AuthInternalError, AuthJoseInitializationError, AuthSecurityError } from "@/shared/errors.ts"
+import {
+ isCryptoKey,
+ isCryptoKeyPair,
+ isCryptoSecret,
+ isEncryptedMode,
+ isJWTPEMFormattedKeyPair,
+ isPEMFormattedKeyPairFromEnv,
+ isSealedMode,
+ isSignedMode,
+} from "@/shared/assert.ts"
export { encoder, getRandomBytes, getSubtleCrypto } from "@aura-stack/jose/crypto"
-import type { User, SessionConfig, JWTKey } from "@/@types/index.ts"
+import type { User, SessionConfig, JWTKey, AsymmetricKeyPairFromEnv } from "@/@types/index.ts"
+import { importPEMKeyPair } from "./shared/crypto.ts"
const getJWTConfig = (config?: SessionConfig) => {
return config?.jwt
@@ -102,7 +112,71 @@ export const verifyMaxExpiration = (payload: TypedJWTPayload>) =>
}
}
-const getSecrets = async (secret: JWTKey, salt: string) => {
+const getSecrets = async (
+ secret: JWTKey | AsymmetricKeyPairFromEnv | { sign: AsymmetricKeyPairFromEnv; encrypt: AsymmetricKeyPairFromEnv },
+ salt: string,
+ session?: SessionConfig
+) => {
+ if (isJWTPEMFormattedKeyPair(secret)) {
+ if (!isSealedMode(session)) {
+ throw new AuthJoseInitializationError(
+ "INVALID_PEM_KEY_PAIR",
+ "Multiples PEM Key Pairs from environment variables require 'sealed' JWT mode. For 'signed' or 'encrypted' modes, provide a single PEM key pair or a combined key object."
+ )
+ }
+
+ const { sign, encrypt } = secret
+ const signingAlg = getEnv("SIGNING_ALG") || getEnv("SIGNING_ALGORITHM") || session?.jwt.signingAlgorithm || "RS256"
+ const encryptionAlg =
+ getEnv("ENCRYPTION_ALG") || getEnv("ENCRYPTION_ALGORITHM") || session?.jwt.keyAlgorithm || "RSA-OAEP-256"
+ const importedSign = await importPEMKeyPair(sign, signingAlg)
+ const importedEncrypt = await importPEMKeyPair(encrypt, encryptionAlg)
+
+ return {
+ jwsSecret: importedSign,
+ jweSecret: importedEncrypt,
+ jwtSecret: {
+ sign: importedSign,
+ encrypt: importedEncrypt,
+ },
+ }
+ }
+ if (isPEMFormattedKeyPairFromEnv(secret)) {
+ if (isSealedMode(session)) {
+ throw new AuthJoseInitializationError(
+ "INVALID_PEM_KEY_PAIR",
+ "Single PEM key pairs from environment variables require 'signed' or 'encrypted' JWT mode. For 'sealed' mode, provide separate signing and encryption keys or a combined key object."
+ )
+ }
+ const algorithm =
+ getEnv("ALGORITHM") ||
+ getEnv("ALG") ||
+ (isSignedMode(session) ? session?.jwt?.signingAlgorithm : undefined) ||
+ (isEncryptedMode(session) ? session?.jwt?.keyAlgorithm : undefined) ||
+ "RS256"
+ const { publicKey, privateKey } = await importPEMKeyPair(secret, algorithm)
+ return {
+ jwsSecret: {
+ publicKey,
+ privateKey,
+ },
+ jweSecret: {
+ publicKey,
+ privateKey,
+ },
+ jwtSecret: {
+ sign: {
+ publicKey,
+ privateKey,
+ },
+ encrypt: {
+ publicKey,
+ privateKey,
+ },
+ },
+ }
+ }
+
if (isCryptoSecret(secret)) {
return {
jwsSecret: secret.sign,
@@ -138,6 +212,36 @@ const getSecrets = async (secret: JWTKey, salt: string) => {
}
}
+const getPEMKeyFromEnv = (prefix: string): AsymmetricKeyPairFromEnv | null => {
+ const publicKey = getEnv(`${prefix}${prefix && "_"}PUBLIC_KEY`)
+ const privateKey = getEnv(`${prefix}${prefix && "_"}PRIVATE_KEY`)
+ if (publicKey && privateKey) {
+ return { publicKey, privateKey }
+ }
+ return null
+}
+
+const getSecretKey = (secret?: JWTKey) => {
+ secret ??= getEnv("SECRET")
+ if (secret) return secret
+ const pem = getPEMKeyFromEnv("")
+ if (pem) {
+ return pem
+ }
+ const signing = getPEMKeyFromEnv("SIGNING")
+ const encryption = getPEMKeyFromEnv("ENCRYPTION")
+ if (signing && encryption) {
+ return {
+ sign: signing,
+ encrypt: encryption,
+ }
+ }
+ throw new AuthInternalError(
+ "JOSE_INITIALIZATION_FAILED",
+ "AURA_AUTH_SECRET environment variable is not set and no secret was provided."
+ )
+}
+
/**
* Creates the JOSE instance used for signing and verifying tokens. It derives keys
* for session tokens and CSRF tokens. For security and determinism, it's required
@@ -153,14 +257,7 @@ const getSecrets = async (secret: JWTKey, salt: string) => {
* @returns jose instance with methods for encoding/decoding JWTs and signing/verifying JWSs
*/
export const createJoseInstance = (secret?: JWTKey, session?: SessionConfig) => {
- secret ??= getEnv("SECRET")
- if (!secret) {
- throw new AuthInternalError(
- "JOSE_INITIALIZATION_FAILED",
- "AURA_AUTH_SECRET environment variable is not set and no secret was provided."
- )
- }
-
+ const secretKey = getSecretKey(secret)
const salt = getEnv("SALT")
if (!salt) {
throw new AuthInternalError(
@@ -179,7 +276,7 @@ export const createJoseInstance = (secret?: JWT
}
const jose = (async () => {
- const { jwsSecret, jweSecret, jwtSecret } = await getSecrets(secret, salt)
+ const { jwsSecret, jweSecret, jwtSecret } = await getSecrets(secretKey, salt, session)
return {
jwt: createJWT(jwtSecret),
diff --git a/packages/core/src/shared/assert.ts b/packages/core/src/shared/assert.ts
index 3da8900b..046e63e9 100644
--- a/packages/core/src/shared/assert.ts
+++ b/packages/core/src/shared/assert.ts
@@ -1,5 +1,12 @@
import { equals, patternToRegex } from "@/shared/utils.ts"
-import type { CryptoSecret, JWTConfig, JWTMode, JWTPayloadWithToken, SessionConfig } from "@/@types/index.ts"
+import type {
+ AsymmetricKeyPairFromEnv,
+ CryptoSecret,
+ JWTConfig,
+ JWTMode,
+ JWTPayloadWithToken,
+ SessionConfig,
+} from "@/@types/index.ts"
export const isFalsy = (value: unknown): boolean => {
return value === false || value === 0 || value === "" || value === null || value === undefined || Number.isNaN(value)
@@ -107,7 +114,7 @@ export const isSignedMode = (config?: SessionConfig): config is { jwt: Extract } =>
getJWTMode(config) === "encrypted"
-export const isSealedMode = (config?: SessionConfig): config is { jwt: Extract } =>
+export const isSealedMode = (config?: SessionConfig): config is { jwt: Extract } =>
getJWTMode(config) === "sealed"
export const isCryptoKeyPair = (value: unknown): value is CryptoKeyPair => {
@@ -128,3 +135,31 @@ export const isCryptoSecret = (value: unknown): value is CryptoSecret => {
(isCryptoKey(value.encrypt) || isCryptoKeyPair(value.encrypt))
)
}
+
+export const isPEMFormattedKey = (value: unknown): value is string => {
+ return typeof value === "string" && /-----BEGIN (PUBLIC|PRIVATE) KEY-----/.test(value)
+}
+
+export const isPEMFormattedKeyPairFromEnv = (value: unknown): value is { publicKey: string; privateKey: string } => {
+ return (
+ typeof value === "object" &&
+ value !== null &&
+ "publicKey" in value &&
+ "privateKey" in value &&
+ isPEMFormattedKey(value.publicKey) &&
+ isPEMFormattedKey(value.privateKey)
+ )
+}
+
+export const isJWTPEMFormattedKeyPair = (
+ value: unknown
+): value is { sign: AsymmetricKeyPairFromEnv; encrypt: AsymmetricKeyPairFromEnv } => {
+ return (
+ typeof value === "object" &&
+ value !== null &&
+ "sign" in value &&
+ "encrypt" in value &&
+ isPEMFormattedKeyPairFromEnv((value as any).sign) &&
+ isPEMFormattedKeyPairFromEnv((value as any).encrypt)
+ )
+}
diff --git a/packages/core/src/shared/crypto.ts b/packages/core/src/shared/crypto.ts
index 5e443273..d5091044 100644
--- a/packages/core/src/shared/crypto.ts
+++ b/packages/core/src/shared/crypto.ts
@@ -1,8 +1,9 @@
import { AuthSecurityError } from "@/shared/errors.ts"
import { isJWTPayloadWithToken } from "@/shared/assert.ts"
import { equals, timingSafeEqual } from "@/shared/utils.ts"
+import { importPKCS8, importSPKI } from "@aura-stack/jose/jose"
import { base64url, encoder, getRandomBytes, getSubtleCrypto } from "@/jose.ts"
-import type { AuthRuntimeConfig, JoseInstance, User } from "@/@types/index.ts"
+import type { AsymmetricKeyPairFromEnv, AuthRuntimeConfig, JoseInstance, User } from "@/@types/index.ts"
export { generateKeyPair as createKeyPair } from "@aura-stack/jose/jose"
@@ -134,3 +135,19 @@ export const verifyPassword = async (password: string, hashedPassword: string) =
return false
}
}
+
+/**
+ * Imports a PEM-formatted asymmetric key pair from strings.
+ *
+ * @param key - An object containing the public and private keys as PEM-formatted strings
+ * @param algorithm - The intended algorithm for the keys (e.g. "RS256" for RSA signing, "RSA-OAEP" for RSA encryption)
+ * @returns A Promise that resolves to a CryptoKeyPair with the imported keys
+ */
+export const importPEMKeyPair = async (key: AsymmetricKeyPairFromEnv, algorithm: string) => {
+ const importedPrivateKey = await importPKCS8(key.privateKey, algorithm, { extractable: true })
+ const importedPublicKey = await importSPKI(key.publicKey, algorithm, { extractable: true })
+ return {
+ publicKey: importedPublicKey,
+ privateKey: importedPrivateKey,
+ }
+}
diff --git a/packages/core/src/shared/errors.ts b/packages/core/src/shared/errors.ts
index 02ecc00f..b5f4f366 100644
--- a/packages/core/src/shared/errors.ts
+++ b/packages/core/src/shared/errors.ts
@@ -91,6 +91,18 @@ export class AuthValidationError extends Error {
}
}
+export class AuthJoseInitializationError extends Error {
+ readonly type = "JOSE_INITIALIZATION_FAILED"
+ readonly code: string
+
+ constructor(code: string, message?: string, options?: ErrorOptions) {
+ super(message, options)
+ this.code = code
+ this.name = new.target.name
+ Error?.captureStackTrace?.(this, new.target)
+ }
+}
+
export const isNativeError = (error: unknown): error is Error => {
return error instanceof Error
}
diff --git a/packages/core/test/env.test.ts b/packages/core/test/env.test.ts
index ce5e3c04..7efb65af 100644
--- a/packages/core/test/env.test.ts
+++ b/packages/core/test/env.test.ts
@@ -1,6 +1,7 @@
import { afterEach, describe, expect, test, vi } from "vitest"
import { env } from "@/shared/env.ts"
import { createSecretValue } from "@/shared/crypto.ts"
+import { exportPKCS8, exportSPKI, generateKeyPair } from "@aura-stack/jose/jose"
describe("env", () => {
afterEach(() => {
@@ -27,4 +28,30 @@ describe("env", () => {
vi.stubEnv("AURA_AUTH_SECRET", secret2)
expect(env.AURA_AUTH_SECRET).toBe(secret2)
})
+
+ test("direct PEM formatted RSA keys", () => {
+ const pem = `-----BEGIN PUBLIC KEY-----
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtGbid5pLu9wsGut7Fg26
+ SPBGntzZk6i17MIf2eS++01/7uq0a59yxYa1AvPbO9OdQDngRup7zPUc7dKivwQe
+ hHTPQ86H9EJkgKqupKj8PUbfzSFzpwcTs9RZmd9o9jBXOkbtH18Pn+jNbqxB0AFA
+ C4ZYfU+l2981U4Xb5UAOfJaqbEWlG0AvW8dqkU1TtAoXtGpoINv46qHF1gSYRDsU
+ AafJaMzLOOC0EhP3U/XZNuAFqgOosGx5iJ1+fyFwMjH+w5iSAXvk1C7KyPTYJDMV
+ JyuDj7yG/TR5yDPS7fyFsbdrLQlwCPqUCmg8Y9lQltXYWu8PptxJYZV4RBbBJ8fr
+ PQIDAQAB
+ -----END PUBLIC KEY-----`
+ vi.stubEnv("AURA_AUTH_SECRET", pem)
+ expect(env.AURA_AUTH_SECRET).toBe(pem)
+ })
+
+ test("PEM formatted RSA keys with newlines", async () => {
+ const keyPair = await generateKeyPair("RS256", { extractable: true })
+ const publicKeyPEM = await exportSPKI(keyPair.publicKey)
+ const privateKeyPEM = await exportPKCS8(keyPair.privateKey)
+ vi.stubEnv("AURA_AUTH_PUBLIC_KEY", publicKeyPEM)
+ vi.stubEnv("AURA_AUTH_PRIVATE_KEY", privateKeyPEM)
+ vi.stubEnv("ALG", "RS256")
+ expect(env.AURA_AUTH_PUBLIC_KEY).toBe(publicKeyPEM)
+ expect(env.AURA_AUTH_PRIVATE_KEY).toBe(privateKeyPEM)
+ expect(env.ALG).toBe("RS256")
+ })
})
diff --git a/packages/core/test/jose.test.ts b/packages/core/test/jose.test.ts
index 5ecf07ba..5bf4eaba 100644
--- a/packages/core/test/jose.test.ts
+++ b/packages/core/test/jose.test.ts
@@ -3,6 +3,7 @@ import { createJoseInstance, encoder } from "@/jose.ts"
import { createSecretValue } from "@/shared/crypto.ts"
import { createAuth } from "@/createAuth.ts"
import { generateKeyPair } from "@aura-stack/jose/jose"
+import { RS256PEMFormat, RSAOAEP256PEMFormat } from "./presets.ts"
const payload = {
sub: "1234567890",
@@ -539,4 +540,82 @@ describe("createJoseInstance", () => {
expect(decoded).toMatchObject(payload)
})
})
+
+ test("PEM formatted RSA keys", async () => {
+ vi.stubEnv("AURA_AUTH_SALT", createSecretValue(32))
+
+ const { publicKey, privateKey } = RS256PEMFormat
+
+ vi.stubEnv("AURA_AUTH_PUBLIC_KEY", publicKey)
+ vi.stubEnv("AURA_AUTH_PRIVATE_KEY", privateKey)
+
+ const jws = createJoseInstance(undefined, {
+ jwt: {
+ mode: "signed",
+ signingAlgorithm: "RS256",
+ },
+ })
+ const signed = await jws.signJWS(payload)
+ const verified = await jws.verifyJWS(signed)
+ expect(verified).toMatchObject(payload)
+
+ const jwe = createJoseInstance(undefined, {
+ jwt: {
+ mode: "encrypted",
+ keyAlgorithm: "RSA-OAEP-256",
+ encryptionAlgorithm: "A256GCM",
+ },
+ })
+
+ const encrypted = await jwe.encryptJWE(payload)
+ const decrypted = await jwe.decryptJWE(encrypted)
+ expect(decrypted).toMatchObject(payload)
+
+ const jwt = createJoseInstance(undefined, {
+ jwt: {
+ mode: "sealed",
+ signingAlgorithm: "RS256",
+ keyAlgorithm: "RSA-OAEP-256",
+ encryptionAlgorithm: "A256GCM",
+ },
+ })
+ await expect(jwt.encodeJWT(payload)).rejects.toThrow(
+ /Single PEM key pairs from environment variables require 'signed' or 'encrypted' JWT mode. For 'sealed' mode, provide separate signing and encryption keys or a combined key object./
+ )
+ })
+
+ test("PEM formatted RSA keys for sealed mode", async () => {
+ vi.stubEnv("AURA_AUTH_SALT", createSecretValue(32))
+
+ const { publicKey: jwsPublicKey, privateKey: jwsPrivateKey } = RS256PEMFormat
+ const { publicKey: jwePublicKey, privateKey: jwePrivateKey } = RSAOAEP256PEMFormat
+
+ vi.stubEnv("AURA_AUTH_SIGNING_PUBLIC_KEY", jwsPublicKey)
+ vi.stubEnv("AURA_AUTH_SIGNING_PRIVATE_KEY", jwsPrivateKey)
+ vi.stubEnv("AURA_AUTH_ENCRYPTION_PUBLIC_KEY", jwePublicKey)
+ vi.stubEnv("AURA_AUTH_ENCRYPTION_PRIVATE_KEY", jwePrivateKey)
+
+ const jwt = createJoseInstance(undefined, {
+ jwt: {
+ mode: "sealed",
+ signingAlgorithm: "RS256",
+ keyAlgorithm: "RSA-OAEP-256",
+ encryptionAlgorithm: "A256GCM",
+ },
+ })
+
+ const token = await jwt.encodeJWT(payload)
+ const decoded = await jwt.decodeJWT(token)
+ expect(decoded).toMatchObject(payload)
+
+ const jws = createJoseInstance(undefined, {
+ jwt: {
+ mode: "signed",
+ signingAlgorithm: "RS256",
+ },
+ })
+ await expect(jws.signJWS(payload)).rejects.toThrow(
+ /Multiples PEM Key Pairs from environment variables require 'sealed' JWT mode. For 'signed' or 'encrypted' modes, provide a single PEM key pair or a combined key object./
+ )
+ })
})
diff --git a/packages/core/test/presets.ts b/packages/core/test/presets.ts
index 01e67318..edd3f189 100644
--- a/packages/core/test/presets.ts
+++ b/packages/core/test/presets.ts
@@ -56,3 +56,99 @@ export const {
jose,
api,
} = auth
+
+const RSA256PublicKey = `-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv5eSIl71g3dyLEFYbv3B
+i93M9nCBLWkbI8mOQLmGgXEj3k92rwfF/+B5gCr1OMUmV+aSLsDvdhDiljQAUpQO
+3ziLaYlk0k8paw7fZjkIejz5BwiWFODTqg9HWSOGr5hfJzyL9gvzaAI2Sp7htei/
+En0u79eRNQNII0dmQtwiMpIEQbisadUEp5+s0Dd7yGUoR18V7pv2A/Ohii8lMUUL
+Efs71Ypf0L5rO9SAhjztxhR6wGWYe+uCNDEF0wuQ/ZL9TvI46Zpf+Z1z+0CzpXYr
+Eloe8oqcCuPIJ1GszZst+qkgFdyo0BXGa1nuA/21ZLmAwUXdzmF1nsGg0J/sUcEQ
+TwIDAQAB
+-----END PUBLIC KEY-----`
+
+const RSA256PrivateKey = `-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC/l5IiXvWDd3Is
+QVhu/cGL3cz2cIEtaRsjyY5AuYaBcSPeT3avB8X/4HmAKvU4xSZX5pIuwO92EOKW
+NABSlA7fOItpiWTSTylrDt9mOQh6PPkHCJYU4NOqD0dZI4avmF8nPIv2C/NoAjZK
+nuG16L8SfS7v15E1A0gjR2ZC3CIykgRBuKxp1QSnn6zQN3vIZShHXxXum/YD86GK
+LyUxRQsR+zvVil/Qvms71ICGPO3GFHrAZZh764I0MQXTC5D9kv1O8jjpml/5nXP7
+QLOldisSWh7yipwK48gnUazNmy36qSAV3KjQFcZrWe4D/bVkuYDBRd3OYXWewaDQ
+n+xRwRBPAgMBAAECggEACh2r77IMck7cUbDexSsTZo0LlUsWzvmya9ib10s60MFs
+1hnJyu91CVyy6maaQJwyn5TpgAJCdbBQWANSRwnq1RqZTDVSBCnkDNm7eukk/otG
+NmEolENteZh8uTY/SZMygJHzIK0iqQm0D/GR0+oZ+JU9seUzlTuuOeQ7TOluY5wR
+i/V6ldrDSOxd2xIKUnxemw0qwbUz0oZ5CKo22K+VksGwa/PempkpyZloSGsE+QR5
+5cZwWxGelzUCDyflOImX+TCKI4IsuBOI+CaQohY3j3xSEunSyE4BCITgtIjlHJOB
+OspOYs/rYQt9xe1ZzBlRTbq/iZAonMgRS1ELm75eWQKBgQD2dlyaZIMpuhKWBWgb
+tPC0CrPLxOqWi5TSkaR+kk389xOqi6m62mPph5dCxv/TrvrXD+v/uST2aAYPnLSY
+3ieVF+KN5fc64M2rgUYR9kOE0ubiir1RI7L7yhYo/bmtSnpd1Wr6vJBU6zEBayTL
+X4Uw+nABrO/Si5SEssb9LGF/RwKBgQDHAZ+iiOhzOJCp9kCqHYJ5ageNVT/Fc85W
+40pYSAiuPlolJV43oG0EnFI7MVkvqSExHfNtk7PgQsahPTsWThJRL4stzbBINmDW
+Fxl505BOoXhnJHLqnQgzmNTinnupumnENImm5ChWbujRREi6kIiZYqUlKjQjN1j1
+9TTGCto6uQKBgQCb6NA30vGuSclMIetz64iBPGv0sYL87RueAQggEYlIRzynnGYo
+j9K4fk/PrHdVf9GqjqXqRUL+pVuAMM+GDLLZfByTSzCUjHVO0x5yamjX81qfYMjW
+NVEaOwK9t5Pn7b9u8H0WVIaxUX7UuOSzyp9FFogYZz/m3ul68GU07whWLQKBgEhP
+Mbb4MiYzpnTrUmG9qTv+p9HV6P8Q7ieqHMhpHCZb55tZsZtawmILfuGdM7/an4He
+VSY6pgBVoyDRQ9f99C/lq5ewBl6my5be+9XFZsj7aOlpWAwhlOpSnP/fACYS4v10
+7ZNjkbieQiBPxHFttQSu0Dzp0dn98WgledB//v2ZAoGAFdsS7VxBMCBcJIgPoYiX
+GDUYZsTiISPnSRqRk1hXkRt1woAa8CK3zsCyrruoCj3VJFk6gb+TWuyBXv4kfRkv
+TGcJZ3AYFKmelXl+1+rRXhe+f79+Z8kRdRSonuG/l1PdtG3P1uglzvksQcSMfOfA
+41a79/alPIgWSGQhEbPxR8I=
+-----END PRIVATE KEY-----`
+
+const RSAOAEP256PublicKey = `-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqFSzD1nqVd91MIYMxqdG
+le/v4BFyNFcdWQgFcxGcfOy+r2tpnGEgN7WmDe/59MGwGP1/al74qWJLvEkqBW/b
+QCEyVX8KcL47p1N88efwR4wBiNseb+qYfdEvb00np8UVhK4QnJlUNHI5t/I9xQr7
+9cieQEGb4C5SzUsetzlVsPq5r0fqOhE8j3mDoGrQO7n6Uc3xssebNNeLTBei4YPr
+2FTbgI9jGYiR8fOgefSSZUAqP7MIhEzpgqBMYQ1IYhoDMR//S5ftye78X266MHCR
+501DT1lkGX3Eem2C8lT8TONyCx4bORIs3401PsfzgEyl4dRvngIE6Qqmvn5zQSkF
+RwIDAQAB
+-----END PUBLIC KEY-----`
+
+const RSAOAEP256PrivateKey = `-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCoVLMPWepV33Uw
+hgzGp0aV7+/gEXI0Vx1ZCAVzEZx87L6va2mcYSA3taYN7/n0wbAY/X9qXvipYku8
+SSoFb9tAITJVfwpwvjunU3zx5/BHjAGI2x5v6ph90S9vTSenxRWErhCcmVQ0cjm3
+8j3FCvv1yJ5AQZvgLlLNSx63OVWw+rmvR+o6ETyPeYOgatA7ufpRzfGyx5s014tM
+F6Lhg+vYVNuAj2MZiJHx86B59JJlQCo/swiETOmCoExhDUhiGgMxH/9Ll+3J7vxf
+browcJHnTUNPWWQZfcR6bYLyVPxM43ILHhs5EizfjTU+x/OATKXh1G+eAgTpCqa+
+fnNBKQVHAgMBAAECggEAOX/ZPHSv5c5zdvRLV+5a36u6qjT7aGKbjUZ+qgxJgqjS
+CBTuWfMZcL41b0xaex9QWnD5PaocUavYiAQL/Rh08eaFDYxcUh/BO8p6gx2Bx8bM
+3WVP89XUaiHzDJdz5MyfKZfV59A+Yb3k9m8iZ3T1lUMGv5dJuh3IvgSbhOXqXg3x
+SE2UqsGGx5/IEvqRcLZ9GMjp/yOPyjwUmqfuN6xPuy698EdLJgzrfX0CCNBvdjsV
+WT2SA1t4pptX+IPikXglsVokv/jEHIAdpNHQDzFLlAL89AxKg9K/7XD+xGkInFVD
+JRKnFcXSS5Nt/n/P1ZnBghDxFwF6DV40hbaVK7+ZTQKBgQDXAlQPMF3XvC9Bah0f
+YSW5yVv3nnr9gQmIcoghUgInOdRTvfagEwkDY1vh5FsQcgQOxyOgbT3yJcQAI32O
+YBWVvaTHy6l4q3PL6ERkplz9aOKCAOH1Ds8JyyiyDtOdE/cylc1Kan+4eg68Lex0
+0kxNb4F5FYaFkyupR1xfh1JBfQKBgQDIbDTfTiDcAn61+d8DfS5HTLZCsaIo6g/Q
+CJ36p70Cs80Js3n0nICOl6TZaIV5qco+VX6H2leQb9xR3ktTDOPUMuDnzf7X8+YO
+y2TOF4phRs47i6S6mamu7rmfAh7Qr8GDiRQXbuX6gFYSFDFsPbLHCn2apOGyCsQF
+VAN7mJ8dEwKBgQCuSRDijw5CxiR4HhAlU5ZFF1gZTLndrC+SD2URvWxJZ7MZfq7f
+6w4vVOcyIO1AU2u+nuXeMS85jitnAV3Rf0l/7A4adpiVXEWtUEXAYKqYL+EMCLMg
+9jQVeD0wuJwIhBqpQoz6eYG2hBpVp9Q4jg+T5YNKJ4y30iheO55BQWwH8QKBgBQE
+ytsrIJkZHrLqfF4K2N6CSQosV/giOOYcljr9GiH095vqc1n9b9HOT8bva7WVQgAr
+5fGH24svwR/kRj3LYc5GLrS4nKXRVL9RjYYQT+AbhGnqLs/8nTg93AiH27AYfgm3
+XWxhxVLaEr7HiZA4MW00HQufQHPaI24s0BQ+UFZFAoGALRr4XkBKSFSZXo5/zO85
+FwWzwouGrZmIlL4flHagvqx94ObCk+aKxR7H2oPdgb0FDIJu7h7XMrZo3T2jOjfp
+3Rs0CexilYo91sin1Bu5/1fYuBEt/AP39t5cjyUhOooE+D+Bn1P6mvKnmZYPDdTR
+B+P4zFojgfEO6TszdVGgCkU=
+-----END PRIVATE KEY-----`
+
+/**
+ * Test-only RSA key pairs for unit testing PEM import functionality.
+ * DO NOT use these keys in production - they are publicly visible.
+ */
+export const RS256PEMFormat = {
+ publicKey: RSA256PublicKey,
+ privateKey: RSA256PrivateKey,
+}
+
+/**
+ * Test-only RSA key pairs for unit testing PEM import functionality.
+ * DO NOT use these keys in production - they are publicly visible.
+ */
+export const RSAOAEP256PEMFormat = {
+ publicKey: RSAOAEP256PublicKey,
+ privateKey: RSAOAEP256PrivateKey,
+}