standard_id 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -2
- data/app/controllers/concerns/standard_id/audience_verification.rb +97 -0
- data/app/controllers/concerns/standard_id/social_authentication.rb +1 -1
- data/app/controllers/concerns/standard_id/web_authentication.rb +11 -1
- data/app/forms/standard_id/web/signup_form.rb +2 -2
- data/app/jobs/standard_id/cleanup_expired_sessions_job.rb +15 -0
- data/lib/standard_id/api/token_manager.rb +2 -5
- data/lib/standard_id/bearer_token_extraction.rb +65 -0
- data/lib/standard_id/engine.rb +5 -0
- data/lib/standard_id/errors.rb +19 -3
- data/lib/standard_id/http_client.rb +15 -4
- data/lib/standard_id/oauth/password_flow.rb +8 -0
- data/lib/standard_id/oauth/token_lifetime_resolver.rb +19 -5
- data/lib/standard_id/version.rb +1 -1
- data/lib/standard_id.rb +1 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a164420e3b36d754be94711721ead8cb4d29efed42fd6e75b0060e1c8c7c3bce
|
|
4
|
+
data.tar.gz: cded0bf7e847cc77ed0f6aa7c69647435c847d62ae02c2af1466778a507f88f4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0b9b506d8014bd8d8cd380b1f5300a87d2d6825e073e8b959c67cbfeac3eb577abe671c9dac330833c8d27ee010b87786f533ac4e247459d691dd956d5053faf
|
|
7
|
+
data.tar.gz: ec1a9973e61d08fff4579099dbcac25ad8355d140994c51c2621559d4aed39486d2fdd891b451ade775799f792c3db19916d555d3a50f3bc84d07ed77c1f0a52
|
data/README.md
CHANGED
|
@@ -787,8 +787,7 @@ class ApplicationController < ActionController::Base
|
|
|
787
787
|
private
|
|
788
788
|
|
|
789
789
|
def handle_account_locked(error)
|
|
790
|
-
# error.
|
|
791
|
-
# error.lock_reason - Why the account was locked
|
|
790
|
+
# error.lock_reason - Why the account was locked (avoid exposing to end users)
|
|
792
791
|
# error.locked_at - When the account was locked
|
|
793
792
|
redirect_to login_path, alert: "Your account has been locked. Please contact support."
|
|
794
793
|
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StandardId
|
|
4
|
+
# Per-controller audience verification for API endpoints.
|
|
5
|
+
#
|
|
6
|
+
# While StandardId validates that the JWT `aud` claim is in the global
|
|
7
|
+
# `allowed_audiences` list, this concern provides additional defense-in-depth
|
|
8
|
+
# by restricting which audiences are accepted by each controller.
|
|
9
|
+
#
|
|
10
|
+
# Requires StandardId::ApiAuthentication to be included before this concern
|
|
11
|
+
# (provides `verify_access_token!` and `current_session`). An error is raised
|
|
12
|
+
# at include time if ApiAuthentication is missing.
|
|
13
|
+
#
|
|
14
|
+
# The caller is responsible for registering `before_action :verify_access_token!`
|
|
15
|
+
# (typically via ApiAuthentication or a base controller). This concern only adds
|
|
16
|
+
# the `verify_audience!` callback, which must run after token verification so
|
|
17
|
+
# that `current_session` is populated. This is consistent with how
|
|
18
|
+
# `require_scopes!` works in ApiAuthentication.
|
|
19
|
+
#
|
|
20
|
+
# @example Single audience
|
|
21
|
+
# class AdminController < Api::BaseController
|
|
22
|
+
# include StandardId::AudienceVerification
|
|
23
|
+
# verify_audience "admin"
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# @example Multiple audiences
|
|
27
|
+
# class SharedController < Api::BaseController
|
|
28
|
+
# include StandardId::AudienceVerification
|
|
29
|
+
# verify_audience "admin", "mobile"
|
|
30
|
+
# end
|
|
31
|
+
module AudienceVerification
|
|
32
|
+
extend ActiveSupport::Concern
|
|
33
|
+
|
|
34
|
+
included do
|
|
35
|
+
unless ancestors.include?(StandardId::ApiAuthentication)
|
|
36
|
+
raise "#{name || 'Controller'} must include StandardId::ApiAuthentication before StandardId::AudienceVerification"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
before_action :verify_audience!
|
|
40
|
+
|
|
41
|
+
rescue_from StandardId::InvalidAudienceError, with: :handle_invalid_audience
|
|
42
|
+
|
|
43
|
+
# Underscore prefix follows Rails class_attribute convention to avoid
|
|
44
|
+
# collisions with application method names.
|
|
45
|
+
class_attribute :_required_audiences, instance_writer: false, default: []
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class_methods do
|
|
49
|
+
# Declare the allowed audiences for this controller.
|
|
50
|
+
# The token's `aud` claim must include at least one of these values.
|
|
51
|
+
#
|
|
52
|
+
# @param audiences [Array<String>] allowed JWT `aud` claim values
|
|
53
|
+
def verify_audience(*audiences)
|
|
54
|
+
self._required_audiences = audiences.flatten.map(&:to_s)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
# Verifies the token's `aud` claim contains at least one of the required audiences.
|
|
61
|
+
# Supports both string and array `aud` claims.
|
|
62
|
+
#
|
|
63
|
+
# @raise [StandardId::InvalidAudienceError] when no audience matches
|
|
64
|
+
def verify_audience!
|
|
65
|
+
return if _required_audiences.empty?
|
|
66
|
+
|
|
67
|
+
# If authentication hasn't run (or token is invalid), let the auth
|
|
68
|
+
# layer handle 401 — don't mask it with a 403.
|
|
69
|
+
return unless current_session
|
|
70
|
+
|
|
71
|
+
token_audiences = Array(current_session.aud)
|
|
72
|
+
return if (token_audiences & _required_audiences).any?
|
|
73
|
+
|
|
74
|
+
raise StandardId::InvalidAudienceError.new(
|
|
75
|
+
required: _required_audiences,
|
|
76
|
+
actual: token_audiences
|
|
77
|
+
)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Returns 403 Forbidden per RFC 6750 §3.1 (insufficient_scope).
|
|
81
|
+
# Includes WWW-Authenticate header per spec, consistent with the gem's
|
|
82
|
+
# 401 handling in Api::BaseController#render_bearer_unauthorized!.
|
|
83
|
+
#
|
|
84
|
+
# The header uses a static description rather than interpolating
|
|
85
|
+
# error.message (which contains raw aud values from the JWT) to
|
|
86
|
+
# avoid header injection via crafted audience strings.
|
|
87
|
+
#
|
|
88
|
+
# Override in your controller for custom error formatting.
|
|
89
|
+
def handle_invalid_audience(error)
|
|
90
|
+
response.set_header(
|
|
91
|
+
"WWW-Authenticate",
|
|
92
|
+
'Bearer error="insufficient_scope", error_description="The access token audience is not permitted for this resource"'
|
|
93
|
+
)
|
|
94
|
+
render json: { error: "insufficient_scope", error_description: error.message }, status: :forbidden
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -47,7 +47,7 @@ module StandardId
|
|
|
47
47
|
account: account,
|
|
48
48
|
value: email
|
|
49
49
|
)
|
|
50
|
-
identifier.verify! if identifier.respond_to?(:verify!)
|
|
50
|
+
identifier.verify! if identifier.respond_to?(:verify!) && [true, "true"].include?(social_info[:email_verified])
|
|
51
51
|
emit_social_account_created(account, provider, social_info)
|
|
52
52
|
account
|
|
53
53
|
end
|
|
@@ -66,7 +66,13 @@ module StandardId
|
|
|
66
66
|
)
|
|
67
67
|
|
|
68
68
|
StandardId::PasswordCredential.find_by(login:).tap do |password_credential|
|
|
69
|
-
|
|
69
|
+
authenticated = password_credential&.authenticate(password)
|
|
70
|
+
|
|
71
|
+
# Perform a dummy bcrypt comparison when the credential doesn't exist
|
|
72
|
+
# to prevent user enumeration via response timing differences.
|
|
73
|
+
BCrypt::Password.new(dummy_password_digest).is_password?(password) unless password_credential
|
|
74
|
+
|
|
75
|
+
unless authenticated
|
|
70
76
|
StandardId::Events.publish(
|
|
71
77
|
StandardId::Events::AUTHENTICATION_FAILED,
|
|
72
78
|
account_lookup: login,
|
|
@@ -103,6 +109,10 @@ module StandardId
|
|
|
103
109
|
@token_manager ||= StandardId::Web::TokenManager.new(request)
|
|
104
110
|
end
|
|
105
111
|
|
|
112
|
+
def dummy_password_digest
|
|
113
|
+
@dummy_password_digest ||= BCrypt::Password.create("").freeze
|
|
114
|
+
end
|
|
115
|
+
|
|
106
116
|
def authentication_guard
|
|
107
117
|
@authentication_guard ||= StandardId::Web::AuthenticationGuard.new
|
|
108
118
|
end
|
|
@@ -37,8 +37,8 @@ module StandardId
|
|
|
37
37
|
rescue ActiveRecord::RecordInvalid => e
|
|
38
38
|
errors.add(:base, e.record.errors.full_messages.join(", "))
|
|
39
39
|
false
|
|
40
|
-
rescue ActiveRecord::RecordNotUnique
|
|
41
|
-
errors.add(:base,
|
|
40
|
+
rescue ActiveRecord::RecordNotUnique
|
|
41
|
+
errors.add(:base, "Unable to complete registration. If you already have an account, please sign in.")
|
|
42
42
|
false
|
|
43
43
|
end
|
|
44
44
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module StandardId
|
|
2
|
+
class CleanupExpiredSessionsJob < ApplicationJob
|
|
3
|
+
queue_as :default
|
|
4
|
+
|
|
5
|
+
# Delete sessions that expired more than `grace_period_seconds` ago.
|
|
6
|
+
# A grace period avoids deleting sessions that just expired and might
|
|
7
|
+
# still be referenced in in-flight requests.
|
|
8
|
+
# Accepts integer seconds for reliable ActiveJob serialization across all queue adapters.
|
|
9
|
+
def perform(grace_period_seconds: 7.days.to_i)
|
|
10
|
+
cutoff = grace_period_seconds.seconds.ago
|
|
11
|
+
deleted = StandardId::Session.where("expires_at < ?", cutoff).delete_all
|
|
12
|
+
Rails.logger.info("[StandardId] Cleaned up #{deleted} expired sessions older than #{cutoff}")
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -30,12 +30,9 @@ module StandardId
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def bearer_token
|
|
33
|
-
return @bearer_token if @bearer_token
|
|
33
|
+
return @bearer_token if defined?(@bearer_token)
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
return unless auth_header&.start_with?("Bearer ")
|
|
37
|
-
|
|
38
|
-
@bearer_token = auth_header.split(" ", 2).last
|
|
35
|
+
@bearer_token = StandardId::BearerTokenExtraction.extract(@request.headers["Authorization"])
|
|
39
36
|
end
|
|
40
37
|
|
|
41
38
|
def verify_jwt_token(token: bearer_token)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StandardId
|
|
4
|
+
# Bearer token extraction utility.
|
|
5
|
+
#
|
|
6
|
+
# This module serves two roles:
|
|
7
|
+
#
|
|
8
|
+
# 1. **Class method** (`BearerTokenExtraction.extract`) — pure extraction
|
|
9
|
+
# logic used by TokenManager in lib/. Lives in lib/ so there is no
|
|
10
|
+
# cross-layer dependency on app/ autoloading.
|
|
11
|
+
#
|
|
12
|
+
# 2. **Controller mixin** (`include StandardId::BearerTokenExtraction`) —
|
|
13
|
+
# provides `extract_bearer_token` as a private instance method.
|
|
14
|
+
# Conventionally, controller concerns live under app/controllers/concerns/,
|
|
15
|
+
# but this module is co-located with the utility to keep the extraction
|
|
16
|
+
# logic in a single file and avoid the same-constant-name conflict
|
|
17
|
+
# between lib/ and app/ autoloading.
|
|
18
|
+
#
|
|
19
|
+
# Does not use ActiveSupport::Concern because it has no `included` or
|
|
20
|
+
# `class_methods` blocks — it is a plain Ruby module.
|
|
21
|
+
#
|
|
22
|
+
# Controllers that include StandardId::ApiAuthentication do NOT need this —
|
|
23
|
+
# token extraction is handled internally by the TokenManager.
|
|
24
|
+
#
|
|
25
|
+
# @example As a controller concern
|
|
26
|
+
# class McpController < ActionController::API
|
|
27
|
+
# include StandardId::BearerTokenExtraction
|
|
28
|
+
#
|
|
29
|
+
# def authenticate!
|
|
30
|
+
# token = extract_bearer_token
|
|
31
|
+
# # validate token...
|
|
32
|
+
# end
|
|
33
|
+
# end
|
|
34
|
+
#
|
|
35
|
+
# @example Direct class method (used by TokenManager)
|
|
36
|
+
# StandardId::BearerTokenExtraction.extract(auth_header)
|
|
37
|
+
module BearerTokenExtraction
|
|
38
|
+
# Extracts the Bearer token from a raw Authorization header value.
|
|
39
|
+
#
|
|
40
|
+
# Note: prior to the introduction of this module, TokenManager#bearer_token
|
|
41
|
+
# returned "" for a bare "Bearer " header. This now returns nil via .presence,
|
|
42
|
+
# which is the correct behavior — downstream JWT parsing receives nil instead
|
|
43
|
+
# of attempting to decode an empty string.
|
|
44
|
+
#
|
|
45
|
+
# @param auth_header [String, nil] the raw Authorization header value
|
|
46
|
+
# @return [String, nil] the bearer token, or nil if not present/empty
|
|
47
|
+
def self.extract(auth_header)
|
|
48
|
+
return unless auth_header&.start_with?("Bearer ")
|
|
49
|
+
|
|
50
|
+
auth_header.split(" ", 2).last.presence
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
# Extracts the token from an "Authorization: Bearer <token>" header.
|
|
56
|
+
# Result is memoized for the lifetime of the controller instance.
|
|
57
|
+
#
|
|
58
|
+
# @return [String, nil] the bearer token, or nil if not present
|
|
59
|
+
def extract_bearer_token
|
|
60
|
+
return @_bearer_token if defined?(@_bearer_token)
|
|
61
|
+
|
|
62
|
+
@_bearer_token = StandardId::BearerTokenExtraction.extract(request.headers["Authorization"])
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
data/lib/standard_id/engine.rb
CHANGED
|
@@ -9,6 +9,11 @@ module StandardId
|
|
|
9
9
|
|
|
10
10
|
StandardId::Events::Subscribers::AccountStatusSubscriber.attach
|
|
11
11
|
StandardId::Events::Subscribers::AccountLockingSubscriber.attach
|
|
12
|
+
|
|
13
|
+
if StandardId.config.issuer.blank?
|
|
14
|
+
Rails.logger.warn("[StandardId] No issuer configured. JWT tokens will not include or verify the 'iss' claim. " \
|
|
15
|
+
"Set StandardId.config.issuer in your initializer for production use.")
|
|
16
|
+
end
|
|
12
17
|
end
|
|
13
18
|
end
|
|
14
19
|
end
|
data/lib/standard_id/errors.rb
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
module StandardId
|
|
2
|
+
# Session errors
|
|
2
3
|
class NotAuthenticatedError < StandardError; end
|
|
3
4
|
|
|
4
5
|
class InvalidSessionError < StandardError; end
|
|
5
6
|
class ExpiredSessionError < InvalidSessionError; end
|
|
6
7
|
class RevokedSessionError < InvalidSessionError; end
|
|
8
|
+
|
|
9
|
+
# Account errors
|
|
7
10
|
class AccountDeactivatedError < StandardError; end
|
|
8
11
|
|
|
9
12
|
class AccountLockedError < StandardError
|
|
10
|
-
|
|
13
|
+
# lock_reason and locked_at are available for logging and admin use.
|
|
14
|
+
# Avoid surfacing lock_reason in user-facing responses.
|
|
15
|
+
attr_reader :lock_reason, :locked_at
|
|
11
16
|
|
|
12
17
|
def initialize(account)
|
|
13
|
-
@account = account
|
|
14
18
|
@lock_reason = account.lock_reason
|
|
15
19
|
@locked_at = account.locked_at
|
|
16
|
-
super("Account has been locked
|
|
20
|
+
super("Account has been locked")
|
|
17
21
|
end
|
|
18
22
|
end
|
|
19
23
|
|
|
24
|
+
# OAuth errors
|
|
20
25
|
class OAuthError < StandardError
|
|
21
26
|
def oauth_error_code
|
|
22
27
|
:invalid_request
|
|
@@ -64,4 +69,15 @@ module StandardId
|
|
|
64
69
|
class UnsupportedResponseTypeError < OAuthError
|
|
65
70
|
def oauth_error_code = :unsupported_response_type
|
|
66
71
|
end
|
|
72
|
+
|
|
73
|
+
# Audience verification errors
|
|
74
|
+
class InvalidAudienceError < StandardError
|
|
75
|
+
attr_reader :required, :actual
|
|
76
|
+
|
|
77
|
+
def initialize(required:, actual:)
|
|
78
|
+
@required = required
|
|
79
|
+
@actual = actual
|
|
80
|
+
super("Token audience [#{actual.join(', ')}] does not match required audiences: #{required.join(', ')}")
|
|
81
|
+
end
|
|
82
|
+
end
|
|
67
83
|
end
|
|
@@ -3,19 +3,30 @@ require "uri"
|
|
|
3
3
|
|
|
4
4
|
module StandardId
|
|
5
5
|
class HttpClient
|
|
6
|
+
OPEN_TIMEOUT = 5
|
|
7
|
+
READ_TIMEOUT = 10
|
|
8
|
+
|
|
6
9
|
class << self
|
|
7
10
|
def post_form(endpoint, params)
|
|
8
11
|
uri = URI(endpoint)
|
|
9
|
-
Net::HTTP.
|
|
12
|
+
request = Net::HTTP::Post.new(uri)
|
|
13
|
+
request.set_form_data(params)
|
|
14
|
+
start_connection(uri) { |http| http.request(request) }
|
|
10
15
|
end
|
|
11
16
|
|
|
12
17
|
def get_with_bearer(endpoint, access_token)
|
|
13
18
|
uri = URI(endpoint)
|
|
14
19
|
request = Net::HTTP::Get.new(uri)
|
|
15
20
|
request["Authorization"] = "Bearer #{access_token}"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
start_connection(uri) { |http| http.request(request) }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def start_connection(uri, &block)
|
|
27
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https",
|
|
28
|
+
open_timeout: OPEN_TIMEOUT,
|
|
29
|
+
read_timeout: READ_TIMEOUT, &block)
|
|
19
30
|
end
|
|
20
31
|
end
|
|
21
32
|
end
|
|
@@ -76,6 +76,10 @@ module StandardId
|
|
|
76
76
|
.includes(credential: :account)
|
|
77
77
|
.find_by(login: username)
|
|
78
78
|
|
|
79
|
+
# Perform a dummy bcrypt comparison when the credential doesn't exist
|
|
80
|
+
# to prevent user enumeration via response timing differences.
|
|
81
|
+
BCrypt::Password.new(dummy_password_digest).is_password?(password) unless @credential
|
|
82
|
+
|
|
79
83
|
@credential&.authenticate(password)&.account
|
|
80
84
|
end
|
|
81
85
|
|
|
@@ -90,6 +94,10 @@ module StandardId
|
|
|
90
94
|
end
|
|
91
95
|
end
|
|
92
96
|
|
|
97
|
+
def dummy_password_digest
|
|
98
|
+
@dummy_password_digest ||= BCrypt::Password.create("").freeze
|
|
99
|
+
end
|
|
100
|
+
|
|
93
101
|
def default_scope
|
|
94
102
|
"read"
|
|
95
103
|
end
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
module StandardId
|
|
2
2
|
module Oauth
|
|
3
3
|
class TokenLifetimeResolver
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
DEFAULT_ACCESS_TOKEN_LIFETIME = 1.hour.to_i
|
|
5
|
+
DEFAULT_REFRESH_TOKEN_LIFETIME = 30.days.to_i
|
|
6
|
+
MAX_ACCESS_TOKEN_LIFETIME = 24.hours.to_i
|
|
7
|
+
MAX_REFRESH_TOKEN_LIFETIME = 90.days.to_i
|
|
7
8
|
|
|
9
|
+
class << self
|
|
8
10
|
def access_token_for(flow_key)
|
|
9
11
|
configured = lookup_token_lifetime(flow_key)
|
|
10
|
-
positive_seconds(configured, default_access_token_lifetime)
|
|
12
|
+
lifetime = positive_seconds(configured, default_access_token_lifetime)
|
|
13
|
+
clamp_seconds(lifetime, MAX_ACCESS_TOKEN_LIFETIME)
|
|
11
14
|
end
|
|
12
15
|
|
|
13
16
|
def refresh_token_lifetime
|
|
14
|
-
positive_seconds(oauth_config.refresh_token_lifetime, DEFAULT_REFRESH_TOKEN_LIFETIME)
|
|
17
|
+
lifetime = positive_seconds(oauth_config.refresh_token_lifetime, DEFAULT_REFRESH_TOKEN_LIFETIME)
|
|
18
|
+
clamp_seconds(lifetime, MAX_REFRESH_TOKEN_LIFETIME)
|
|
15
19
|
end
|
|
16
20
|
|
|
17
21
|
private
|
|
@@ -41,6 +45,16 @@ module StandardId
|
|
|
41
45
|
(normalized_value.positive? ? normalized_value : fallback_value).seconds
|
|
42
46
|
end
|
|
43
47
|
|
|
48
|
+
def clamp_seconds(duration, max)
|
|
49
|
+
seconds = duration.to_i
|
|
50
|
+
if seconds > max
|
|
51
|
+
Rails.logger.warn { "[StandardId] Token lifetime #{seconds}s exceeds maximum #{max}s, clamping to #{max}s" }
|
|
52
|
+
max.seconds
|
|
53
|
+
else
|
|
54
|
+
duration
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
44
58
|
def oauth_config
|
|
45
59
|
StandardId.config.oauth
|
|
46
60
|
end
|
data/lib/standard_id/version.rb
CHANGED
data/lib/standard_id.rb
CHANGED
|
@@ -13,6 +13,7 @@ require "standard_id/events/subscribers/account_locking_subscriber"
|
|
|
13
13
|
require "standard_id/account_status"
|
|
14
14
|
require "standard_id/account_locking"
|
|
15
15
|
require "standard_id/http_client"
|
|
16
|
+
require "standard_id/bearer_token_extraction"
|
|
16
17
|
require "standard_id/jwt_service"
|
|
17
18
|
require "standard_id/web/session_manager"
|
|
18
19
|
require "standard_id/web/token_manager"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: standard_id
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jaryl Sim
|
|
@@ -80,6 +80,7 @@ files:
|
|
|
80
80
|
- app/assets/stylesheets/standard_id/application.css
|
|
81
81
|
- app/channels/concerns/standard_id/cable_authentication.rb
|
|
82
82
|
- app/controllers/concerns/standard_id/api_authentication.rb
|
|
83
|
+
- app/controllers/concerns/standard_id/audience_verification.rb
|
|
83
84
|
- app/controllers/concerns/standard_id/inertia_rendering.rb
|
|
84
85
|
- app/controllers/concerns/standard_id/inertia_support.rb
|
|
85
86
|
- app/controllers/concerns/standard_id/passwordless_strategy.rb
|
|
@@ -117,6 +118,7 @@ files:
|
|
|
117
118
|
- app/forms/standard_id/web/signup_form.rb
|
|
118
119
|
- app/helpers/standard_id/application_helper.rb
|
|
119
120
|
- app/jobs/standard_id/application_job.rb
|
|
121
|
+
- app/jobs/standard_id/cleanup_expired_sessions_job.rb
|
|
120
122
|
- app/mailers/standard_id/application_mailer.rb
|
|
121
123
|
- app/models/concerns/standard_id/account_associations.rb
|
|
122
124
|
- app/models/concerns/standard_id/credentiable.rb
|
|
@@ -171,6 +173,7 @@ files:
|
|
|
171
173
|
- lib/standard_id/api/session_manager.rb
|
|
172
174
|
- lib/standard_id/api/token_manager.rb
|
|
173
175
|
- lib/standard_id/api_engine.rb
|
|
176
|
+
- lib/standard_id/bearer_token_extraction.rb
|
|
174
177
|
- lib/standard_id/config/schema.rb
|
|
175
178
|
- lib/standard_id/current_attributes.rb
|
|
176
179
|
- lib/standard_id/engine.rb
|