Class: WOWSQL::ProjectAuthClient
- Inherits:
-
Object
- Object
- WOWSQL::ProjectAuthClient
- Defined in:
- lib/wowsql/auth.rb
Overview
Project-level authentication client.
UNIFIED AUTHENTICATION: Uses the same API keys (anon/service) as database operations. One project = one set of keys for ALL operations (auth + database).
Key Types:
- Anonymous Key (wowsql_anon_...): For client-side auth operations
- Service Role Key (wowsql_service_...): For server-side auth operations
Instance Attribute Summary collapse
-
#base_url ⇒ Object
readonly
Returns the value of attribute base_url.
Instance Method Summary collapse
-
#change_password(current_password:, new_password:, access_token: nil) ⇒ Hash
Change the authenticated user’s password.
-
#clear_session ⇒ Object
Clear session tokens.
-
#close ⇒ Object
Close the HTTP connection.
-
#exchange_oauth_callback(provider:, code:, redirect_uri: nil) ⇒ AuthResponse
Exchange OAuth callback code for access tokens.
-
#forgot_password(email:) ⇒ Hash
Request password reset.
-
#get_oauth_authorization_url(provider:, redirect_uri: nil) ⇒ Hash
Get OAuth authorization URL.
-
#get_session ⇒ Hash
Get current session tokens.
-
#get_user(access_token: nil) ⇒ AuthUser
Get current authenticated user.
-
#initialize(project_url, api_key, base_domain: 'wowsqlconnect.com', secure: true, timeout: 30, verify_ssl: true, token_storage: nil) ⇒ ProjectAuthClient
constructor
A new instance of ProjectAuthClient.
-
#logout(access_token: nil) ⇒ Hash
Logout the current user by invalidating their session.
-
#refresh_token(refresh_token: nil) ⇒ AuthResponse
Exchange a refresh token for new access + refresh tokens.
-
#resend_verification(email:) ⇒ Hash
Resend verification email.
-
#reset_password(token:, new_password:) ⇒ Hash
Reset password with token.
-
#send_magic_link(email:, purpose: 'login') ⇒ Hash
Send magic link to user’s email.
-
#send_otp(email:, purpose: 'login') ⇒ Hash
Send OTP code to user’s email.
-
#set_session(access_token:, refresh_token: nil) ⇒ Object
Set session tokens.
-
#sign_in(email:, password:) ⇒ AuthResponse
Sign in an existing user.
-
#sign_up(email:, password:, full_name: nil, user_metadata: nil) ⇒ AuthResponse
Sign up a new user.
-
#update_user(full_name: nil, avatar_url: nil, username: nil, user_metadata: nil, access_token: nil) ⇒ AuthUser
Update the authenticated user’s profile.
-
#verify_email(token:) ⇒ Hash
Verify email using token.
-
#verify_otp(email:, otp:, purpose: 'login', new_password: nil) ⇒ AuthResponse, Hash
Verify OTP and complete authentication.
Constructor Details
#initialize(project_url, api_key, base_domain: 'wowsqlconnect.com', secure: true, timeout: 30, verify_ssl: true, token_storage: nil) ⇒ ProjectAuthClient
Returns a new instance of ProjectAuthClient.
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/wowsql/auth.rb', line 99 def initialize(project_url, api_key, base_domain: 'wowsqlconnect.com', secure: true, timeout: 30, verify_ssl: true, token_storage: nil) @api_key = api_key @timeout = timeout @base_url = build_auth_base_url(project_url, base_domain, secure) @storage = token_storage || MemoryTokenStorage.new @access_token = @storage.get_access_token @refresh_token = @storage.get_refresh_token = verify_ssl ? {} : { verify: false } @conn = Faraday.new(url: @base_url, ssl: ) do |f| f.request :json f.response :json f.adapter Faraday.default_adapter f..timeout = timeout end @conn.headers['Authorization'] = "Bearer #{api_key}" @conn.headers['Content-Type'] = 'application/json' end |
Instance Attribute Details
#base_url ⇒ Object (readonly)
Returns the value of attribute base_url.
90 91 92 |
# File 'lib/wowsql/auth.rb', line 90 def base_url @base_url end |
Instance Method Details
#change_password(current_password:, new_password:, access_token: nil) ⇒ Hash
Change the authenticated user’s password.
373 374 375 376 377 378 379 380 381 382 |
# File 'lib/wowsql/auth.rb', line 373 def change_password(current_password:, new_password:, access_token: nil) token = access_token || @access_token || @storage.get_access_token raise WOWSQLError.new('Access token is required. Call sign_in first.') unless token request_with_headers( 'POST', '/change-password', nil, { current_password: current_password, new_password: new_password }, 'Authorization' => "Bearer #{token}" ) end |
#clear_session ⇒ Object
Clear session tokens.
429 430 431 432 433 434 |
# File 'lib/wowsql/auth.rb', line 429 def clear_session @access_token = nil @refresh_token = nil @storage.set_access_token(nil) @storage.set_refresh_token(nil) end |
#close ⇒ Object
Close the HTTP connection.
437 438 439 |
# File 'lib/wowsql/auth.rb', line 437 def close @conn.close if @conn.respond_to?(:close) end |
#exchange_oauth_callback(provider:, code:, redirect_uri: nil) ⇒ AuthResponse
Exchange OAuth callback code for access tokens.
208 209 210 211 212 213 214 215 216 |
# File 'lib/wowsql/auth.rb', line 208 def exchange_oauth_callback(provider:, code:, redirect_uri: nil) payload = { code: code } payload[:redirect_uri] = redirect_uri if redirect_uri data = request('POST', "/oauth/#{provider}/callback", nil, payload) session = persist_session(data) user = data['user'] ? AuthUser.new(**normalize_user(data['user'])) : nil AuthResponse.new(session: session, user: user) end |
#forgot_password(email:) ⇒ Hash
Request password reset.
222 223 224 225 226 227 228 229 |
# File 'lib/wowsql/auth.rb', line 222 def forgot_password(email:) payload = { email: email } data = request('POST', '/forgot-password', nil, payload) { 'success' => data['success'] != false, 'message' => data['message'] || 'If that email exists, a password reset link has been sent' } end |
#get_oauth_authorization_url(provider:, redirect_uri: nil) ⇒ Hash
Get OAuth authorization URL.
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/wowsql/auth.rb', line 169 def (provider:, redirect_uri: nil) raise WOWSQLError.new('provider is required and cannot be empty') if provider.nil? || provider.strip.empty? provider = provider.strip params = {} params['frontend_redirect_uri'] = redirect_uri.strip if redirect_uri begin data = request('GET', "/oauth/#{provider}", params, nil) { 'authorization_url' => data['authorization_url'] || '', 'provider' => data['provider'] || provider, 'backend_callback_url' => data['backend_callback_url'] || '', 'frontend_redirect_uri' => data['frontend_redirect_uri'] || redirect_uri || '' } rescue WOWSQLError => e if e.status_code == 502 raise WOWSQLError.new( "Bad Gateway (502): The backend server may be down or unreachable. " \ "Check if the backend is running and accessible at #{@base_url}", 502, e.response ) elsif e.status_code == 400 raise WOWSQLError.new( "Bad Request (400): #{e.}. " \ "Ensure OAuth provider '#{provider}' is configured and enabled for this project.", 400, e.response ) end raise end end |
#get_session ⇒ Hash
Get current session tokens.
410 411 412 413 414 415 |
# File 'lib/wowsql/auth.rb', line 410 def get_session { 'access_token' => @access_token || @storage.get_access_token, 'refresh_token' => @refresh_token || @storage.get_refresh_token } end |
#get_user(access_token: nil) ⇒ AuthUser
Get current authenticated user.
156 157 158 159 160 161 162 |
# File 'lib/wowsql/auth.rb', line 156 def get_user(access_token: nil) token = access_token || @access_token || @storage.get_access_token raise WOWSQLError.new('Access token is required. Call sign_in first.') unless token data = request_with_headers('GET', '/me', nil, nil, 'Authorization' => "Bearer #{token}") AuthUser.new(**normalize_user(data)) end |
#logout(access_token: nil) ⇒ Hash
Logout the current user by invalidating their session.
345 346 347 348 349 350 351 352 |
# File 'lib/wowsql/auth.rb', line 345 def logout(access_token: nil) token = access_token || @access_token || @storage.get_access_token raise WOWSQLError.new('Access token is required. Call sign_in first.') unless token data = request_with_headers('POST', '/logout', nil, nil, 'Authorization' => "Bearer #{token}") clear_session data end |
#refresh_token(refresh_token: nil) ⇒ AuthResponse
Exchange a refresh token for new access + refresh tokens.
358 359 360 361 362 363 364 365 |
# File 'lib/wowsql/auth.rb', line 358 def refresh_token(refresh_token: nil) token = refresh_token || @refresh_token || @storage.get_refresh_token raise WOWSQLError.new('Refresh token is required. Call sign_in first.') unless token data = request('POST', '/refresh-token', nil, { refresh_token: token }) session = persist_session(data) AuthResponse.new(session: session, user: nil) end |
#resend_verification(email:) ⇒ Hash
Resend verification email.
332 333 334 335 336 337 338 339 |
# File 'lib/wowsql/auth.rb', line 332 def resend_verification(email:) payload = { email: email } data = request('POST', '/resend-verification', nil, payload) { 'success' => data['success'] != false, 'message' => data['message'] || 'If that email exists, a verification email has been sent' } end |
#reset_password(token:, new_password:) ⇒ Hash
Reset password with token.
236 237 238 239 240 241 242 243 |
# File 'lib/wowsql/auth.rb', line 236 def reset_password(token:, new_password:) payload = { token: token, new_password: new_password } data = request('POST', '/reset-password', nil, payload) { 'success' => data['success'] != false, 'message' => data['message'] || 'Password reset successfully! You can now login with your new password' } end |
#send_magic_link(email:, purpose: 'login') ⇒ Hash
Send magic link to user’s email.
301 302 303 304 305 306 307 308 309 310 311 312 |
# File 'lib/wowsql/auth.rb', line 301 def send_magic_link(email:, purpose: 'login') unless %w[login signup email_verification].include?(purpose) raise WOWSQLError.new("Purpose must be 'login', 'signup', or 'email_verification'") end payload = { email: email, purpose: purpose } data = request('POST', '/magic-link/send', nil, payload) { 'success' => data['success'] != false, 'message' => data['message'] || 'If that email exists, a magic link has been sent' } end |
#send_otp(email:, purpose: 'login') ⇒ Hash
Send OTP code to user’s email.
250 251 252 253 254 255 256 257 258 259 260 261 |
# File 'lib/wowsql/auth.rb', line 250 def send_otp(email:, purpose: 'login') unless %w[login signup password_reset].include?(purpose) raise WOWSQLError.new("Purpose must be 'login', 'signup', or 'password_reset'") end payload = { email: email, purpose: purpose } data = request('POST', '/otp/send', nil, payload) { 'success' => data['success'] != false, 'message' => data['message'] || 'If that email exists, an OTP code has been sent' } end |
#set_session(access_token:, refresh_token: nil) ⇒ Object
Set session tokens.
421 422 423 424 425 426 |
# File 'lib/wowsql/auth.rb', line 421 def set_session(access_token:, refresh_token: nil) @access_token = access_token @refresh_token = refresh_token @storage.set_access_token(access_token) @storage.set_refresh_token(refresh_token) end |
#sign_in(email:, password:) ⇒ AuthResponse
Sign in an existing user.
145 146 147 148 149 150 |
# File 'lib/wowsql/auth.rb', line 145 def sign_in(email:, password:) payload = { email: email, password: password } data = request('POST', '/login', nil, payload) session = persist_session(data) AuthResponse.new(session: session, user: nil) end |
#sign_up(email:, password:, full_name: nil, user_metadata: nil) ⇒ AuthResponse
Sign up a new user.
129 130 131 132 133 134 135 136 137 138 |
# File 'lib/wowsql/auth.rb', line 129 def sign_up(email:, password:, full_name: nil, user_metadata: nil) payload = { email: email, password: password } payload[:full_name] = full_name if full_name payload[:user_metadata] = if data = request('POST', '/signup', nil, payload) session = persist_session(data) user = data['user'] ? AuthUser.new(**normalize_user(data['user'])) : nil AuthResponse.new(session: session, user: user) end |
#update_user(full_name: nil, avatar_url: nil, username: nil, user_metadata: nil, access_token: nil) ⇒ AuthUser
Update the authenticated user’s profile.
392 393 394 395 396 397 398 399 400 401 402 403 404 405 |
# File 'lib/wowsql/auth.rb', line 392 def update_user(full_name: nil, avatar_url: nil, username: nil, user_metadata: nil, access_token: nil) token = access_token || @access_token || @storage.get_access_token raise WOWSQLError.new('Access token is required. Call sign_in first.') unless token payload = {} payload[:full_name] = full_name unless full_name.nil? payload[:avatar_url] = avatar_url unless avatar_url.nil? payload[:username] = username unless username.nil? payload[:user_metadata] = unless .nil? raise WOWSQLError.new('At least one field to update is required') if payload.empty? data = request_with_headers('PATCH', '/me', nil, payload, 'Authorization' => "Bearer #{token}") AuthUser.new(**normalize_user(data)) end |
#verify_email(token:) ⇒ Hash
Verify email using token.
318 319 320 321 322 323 324 325 326 |
# File 'lib/wowsql/auth.rb', line 318 def verify_email(token:) payload = { token: token } data = request('POST', '/verify-email', nil, payload) { 'success' => data['success'] != false, 'message' => data['message'] || 'Email verified successfully!', 'user' => data['user'] ? AuthUser.new(**normalize_user(data['user'])) : nil } end |
#verify_otp(email:, otp:, purpose: 'login', new_password: nil) ⇒ AuthResponse, Hash
Verify OTP and complete authentication.
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 |
# File 'lib/wowsql/auth.rb', line 270 def verify_otp(email:, otp:, purpose: 'login', new_password: nil) unless %w[login signup password_reset].include?(purpose) raise WOWSQLError.new("Purpose must be 'login', 'signup', or 'password_reset'") end if purpose == 'password_reset' && new_password.nil? raise WOWSQLError.new('new_password is required for password_reset purpose') end payload = { email: email, otp: otp, purpose: purpose } payload[:new_password] = new_password if new_password data = request('POST', '/otp/verify', nil, payload) if purpose == 'password_reset' return { 'success' => data['success'] != false, 'message' => data['message'] || 'Password reset successfully! You can now login with your new password' } end session = persist_session(data) user = data['user'] ? AuthUser.new(**normalize_user(data['user'])) : nil AuthResponse.new(session: session, user: user) end |