Module: Tep::AuthOAuth2
- Defined in:
- lib/tep/auth_oauth2.rb
Constant Summary collapse
- DEFAULT_CODE_TTL =
Default code TTL (seconds). Apps that need shorter / longer pass an explicit ttl_seconds to issue_code.
600- DEFAULT_TOKEN_TTL =
Default token TTL (seconds). The JWT exp claim is set to ‘now + this`. Apps that need a different window pass an explicit token_ttl_seconds to exchange_code.
3600
Class Method Summary collapse
-
.exchange_code(code, client_id, token_ttl_seconds) ⇒ Object
Redeem a code for a JWT.
- .find_client(client_id) ⇒ Object
-
.issue_code(principal_id, client_id, caps_str, ttl_seconds) ⇒ Object
Mint a one-time code tied to (principal, client, granted_caps).
-
.mint_jwt(rec, token_ttl_seconds) ⇒ Object
Build the JWT payload and sign it.
-
.register_client(client_id, name, redirect_uri, allowed_caps) ⇒ Object
Register a client (bot / agent / automation peer) with the authorization server.
-
.sweep_expired_codes ⇒ Object
Walk the code registry, drop entries whose expires_at has passed.
- .unregister_client(client_id) ⇒ Object
Class Method Details
.exchange_code(code, client_id, token_ttl_seconds) ⇒ Object
Redeem a code for a JWT. The code MUST have been issued for this exact client_id (no cross-client redemption). Returns the JWT string on success, “” on failure (unknown code, client_id mismatch, expired, already-redeemed).
The JWT is single-use against the registry: a successful exchange_code removes the code from the registry.
‘token_ttl_seconds` is the JWT’s exp lifetime; pass 0 for DEFAULT_TOKEN_TTL.
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/tep/auth_oauth2.rb', line 122 def self.exchange_code(code, client_id, token_ttl_seconds) Tep::AuthOAuth2.sweep_expired_codes codes = Tep::APP.auth_oauth2_codes idx = -1 i = 0 while i < codes.length if codes[i].code == code && codes[i].client_id == client_id idx = i i = codes.length else i += 1 end end if idx < 0 return "" end rec = codes[idx] codes.delete_at(idx) if rec.expired?(Time.now.to_i) return "" end Tep::AuthOAuth2.mint_jwt(rec, token_ttl_seconds) end |
.find_client(client_id) ⇒ Object
75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/tep/auth_oauth2.rb', line 75 def self.find_client(client_id) clients = Tep::APP.auth_oauth2_clients i = 0 while i < clients.length if clients[i].client_id == client_id return clients[i] end i += 1 end nil end |
.issue_code(principal_id, client_id, caps_str, ttl_seconds) ⇒ Object
Mint a one-time code tied to (principal, client, granted_caps). Caller (the app’s /authorize handler) is responsible for validating that granted_caps is a subset of the client’s allowed_caps before calling – the issuance surface itself trusts the caller.
‘caps_str` is comma-separated (matches Tep::AuthBearerToken’s wire format). ‘ttl_seconds` is the lifetime; pass 0 for DEFAULT_CODE_TTL.
Returns the opaque code string (base64url, ~32 chars).
98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/tep/auth_oauth2.rb', line 98 def self.issue_code(principal_id, client_id, caps_str, ttl_seconds) Tep::AuthOAuth2.sweep_expired_codes ttl = ttl_seconds if ttl <= 0 ttl = DEFAULT_CODE_TTL end code = Crypto.sp_crypto_random_b64url(24) expires_at = Time.now.to_i + ttl rec = Tep::AuthOAuth2Code.new( code, principal_id, client_id, caps_str, expires_at) Tep::APP.auth_oauth2_codes.push(rec) code end |
.mint_jwt(rec, token_ttl_seconds) ⇒ Object
Build the JWT payload and sign it. Uses Tep::Jwt with the same shared secret as Tep::AuthBearerToken, so apps don’t need to manage a second secret – one HS256 secret signs all tokens regardless of issuance path.
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/tep/auth_oauth2.rb', line 150 def self.mint_jwt(rec, token_ttl_seconds) secret = Tep::APP.auth_bearer_secret if secret.length == 0 return "" end ttl = token_ttl_seconds if ttl <= 0 ttl = DEFAULT_TOKEN_TTL end now_ts = Time.now.to_i exp_ts = now_ts + ttl delegate_str = rec.client_id + "|" + now_ts.to_s + "|" + exp_ts.to_s + "|oauth_grant" payload = "{" + Tep::Json.encode_pair_str("sub", rec.principal_id) + "," + Tep::Json.encode_pair_int("exp", exp_ts) + "," + Tep::Json.encode_pair_str("caps", rec.caps_str) + "," + Tep::Json.encode_pair_str("delegate", delegate_str) + "}" Tep::Jwt.encode_hs256(payload, secret) end |
.register_client(client_id, name, redirect_uri, allowed_caps) ⇒ Object
Register a client (bot / agent / automation peer) with the authorization server. Subsequent issue_code and exchange_code calls reference it by client_id. Re-registering an existing client_id replaces the prior entry.
54 55 56 57 58 59 60 |
# File 'lib/tep/auth_oauth2.rb', line 54 def self.register_client(client_id, name, redirect_uri, allowed_caps) Tep::AuthOAuth2.unregister_client(client_id) client = Tep::AuthOAuth2Client.new( client_id, name, redirect_uri, allowed_caps) Tep::APP.auth_oauth2_clients.push(client) 0 end |
.sweep_expired_codes ⇒ Object
Walk the code registry, drop entries whose expires_at has passed. Called on every issue / exchange so the registry doesn’t grow unboundedly even without explicit pruning. Back-to-front so delete_at indices stay valid mid-loop.
176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/tep/auth_oauth2.rb', line 176 def self.sweep_expired_codes codes = Tep::APP.auth_oauth2_codes now_ts = Time.now.to_i i = codes.length - 1 while i >= 0 if codes[i].expired?(now_ts) codes.delete_at(i) end i -= 1 end 0 end |
.unregister_client(client_id) ⇒ Object
62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/tep/auth_oauth2.rb', line 62 def self.unregister_client(client_id) clients = Tep::APP.auth_oauth2_clients i = 0 while i < clients.length if clients[i].client_id == client_id clients.delete_at(i) return 0 end i += 1 end 0 end |