Module: MailMCP::JwtService
- Defined in:
- lib/mail_mcp/jwt_service.rb
Defined Under Namespace
Classes: Error
Constant Summary collapse
- DEFAULT_EXPIRY =
8 * 3600
- DEFAULT_REFRESH_EXPIRY =
30 * 24 * 3600
- CRED_KEYS =
%w[ imap_host imap_port imap_ssl imap_username imap_password smtp_host smtp_port smtp_ssl smtp_username smtp_password ].freeze
- CODE_EXPIRY =
Authorization code — short-lived JWE carrying creds + PKCE state (stateless PKCE)
300
Class Method Summary collapse
- .decode_client_id(token) ⇒ Object
-
.issue(creds, expires_in: DEFAULT_EXPIRY) ⇒ Object
Access token — JWE (dir/A256GCM), credentials embedded directly in payload.
-
.issue_client_id(imap_host:, imap_port:, imap_ssl:, smtp_host:, smtp_port:, smtp_ssl:, client_secret:) ⇒ Object
Client ID token — JWE, no expiry, carries imap/smtp config + client_secret.
-
.issue_code(creds:, code_challenge:, redirect_uri:, client_id:) ⇒ Object
5 minutes.
-
.issue_refresh(creds, expires_in: DEFAULT_REFRESH_EXPIRY) ⇒ Object
Refresh token — JWE, longer-lived, carries same credential payload.
- .verify(token) ⇒ Object
- .verify_code(token) ⇒ Object
- .verify_refresh(token) ⇒ Object
Class Method Details
.decode_client_id(token) ⇒ Object
78 79 80 81 82 83 |
# File 'lib/mail_mcp/jwt_service.rb', line 78 def self.decode_client_id(token) payload = decode_jwe(token, verify_exp: false) raise Error, "Not a client_id token" unless payload["typ"] == "client_id" payload end |
.issue(creds, expires_in: DEFAULT_EXPIRY) ⇒ Object
Access token — JWE (dir/A256GCM), credentials embedded directly in payload
16 17 18 |
# File 'lib/mail_mcp/jwt_service.rb', line 16 def self.issue(creds, expires_in: DEFAULT_EXPIRY) issue_jwe(creds.merge("typ" => "access"), expires_in: expires_in) end |
.issue_client_id(imap_host:, imap_port:, imap_ssl:, smtp_host:, smtp_port:, smtp_ssl:, client_secret:) ⇒ Object
Client ID token — JWE, no expiry, carries imap/smtp config + client_secret
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/mail_mcp/jwt_service.rb', line 62 def self.issue_client_id(imap_host:, imap_port:, imap_ssl:, smtp_host:, smtp_port:, smtp_ssl:, client_secret:) payload = JSON.generate( iss: ENV.fetch("BASE_URL"), aud: ENV.fetch("BASE_URL"), typ: "client_id", imap_host: imap_host, imap_port: imap_port.to_i, imap_ssl: imap_ssl, smtp_host: smtp_host, smtp_port: smtp_port.to_i, smtp_ssl: smtp_ssl, cs: client_secret ) encrypt_jwe(payload) end |
.issue_code(creds:, code_challenge:, redirect_uri:, client_id:) ⇒ Object
5 minutes
42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/mail_mcp/jwt_service.rb', line 42 def self.issue_code(creds:, code_challenge:, redirect_uri:, client_id:) issue_jwe( creds.merge( "typ" => "code", "code_challenge" => code_challenge, "redirect_uri" => redirect_uri, "client_id" => client_id ), expires_in: CODE_EXPIRY ) end |
.issue_refresh(creds, expires_in: DEFAULT_REFRESH_EXPIRY) ⇒ Object
Refresh token — JWE, longer-lived, carries same credential payload
28 29 30 |
# File 'lib/mail_mcp/jwt_service.rb', line 28 def self.issue_refresh(creds, expires_in: DEFAULT_REFRESH_EXPIRY) issue_jwe(creds.merge("typ" => "refresh"), expires_in: expires_in) end |
.verify(token) ⇒ Object
20 21 22 23 24 25 |
# File 'lib/mail_mcp/jwt_service.rb', line 20 def self.verify(token) payload = decode_jwe(token) raise Error, "Not an access token" unless payload["typ"] == "access" payload.slice(*CRED_KEYS) end |
.verify_code(token) ⇒ Object
54 55 56 57 58 59 |
# File 'lib/mail_mcp/jwt_service.rb', line 54 def self.verify_code(token) payload = decode_jwe(token) raise Error, "Not an authorization code" unless payload["typ"] == "code" payload end |