foobara-auth 0.0.5 → 0.0.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8974dadf886b62fb510f52275ee227a95d74e1fe57b3f8b1fff7242efa3b0949
4
- data.tar.gz: 01df20b29400198bf1ffa231586a4f6472748d166598ab088a49b3c116ccb236
3
+ metadata.gz: 1aa9644452f2df69c840be3976c4ea0c9b4d7af09ae432a20c98bdb71f87393e
4
+ data.tar.gz: f28a748760a24e913af0f53d47be631aaaa93394e5747978f2cd22f42048bd3b
5
5
  SHA512:
6
- metadata.gz: ebbbfd838051b025b66fd40cd324db2037de76a7ea86f72630269697e262071dc4551f0039d2646fffe7a774b38c798c38728d369790b208a7e61eab353566ca
7
- data.tar.gz: 7c14f7e424f2ed8e803d94c402a74b971f4dd641cfabba5fc49b0a155d13f6ad6eb6b086582e4ac2f22dea8c76b7d81fb597a0c72ca0fc91f744f6564fb54d4f
6
+ metadata.gz: dad7b1b7193abe3753b6d7f592a1b2fcd5c7c9b229c5025dbc3ecffb645c97c195eb6199a410a9b49fd70e032c05349d298b3fd153a78bebee9862df5da64ff1
7
+ data.tar.gz: 04a75cf7418015a6399442c2b0cd6244cfd1410b747f49adaa2ca7cf5da03f8c511b4c8caa38bfdf4d849c1edfb5cb270a2edac685475d0185dd0917bee5394c
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## [0.0.7] - 2025-03-31
2
+
3
+ - Support creating a user with a specific user_id
4
+
5
+ ## [0.0.6] - 2025-03-31
6
+
7
+ - Add Logout command
8
+ - DRY up Login/RefreshLogin
9
+
1
10
  ## [0.0.5] - 2025-03-29
2
11
 
3
12
  - Fix bug duplicating refresh tokens
@@ -0,0 +1,46 @@
1
+ require "jwt"
2
+
3
+ module Foobara
4
+ module Auth
5
+ class BuildAccessToken < Foobara::Command
6
+ inputs do
7
+ user Types::User, :allow_nil
8
+ token_ttl :integer, default: 30 * 60
9
+ end
10
+
11
+ result :string, :sensitive_exposed
12
+
13
+ def execute
14
+ determine_timestamps
15
+ generate_access_token
16
+
17
+ access_token
18
+ end
19
+
20
+ attr_accessor :access_token, :expires_at
21
+
22
+ def determine_timestamps
23
+ now = Time.now
24
+ self.expires_at = now + token_ttl
25
+ end
26
+
27
+ def generate_access_token
28
+ payload = { sub: user.id, exp: expires_at.to_i }
29
+
30
+ self.access_token = JWT.encode(payload, jwt_secret, "HS256")
31
+ end
32
+
33
+ def jwt_secret
34
+ jwt_secret_text = ENV.fetch("JWT_SECRET", nil)
35
+
36
+ unless jwt_secret_text
37
+ # :nocov:
38
+ raise "You must set the JWT_SECRET environment variable"
39
+ # :nocov:
40
+ end
41
+
42
+ jwt_secret_text
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,51 @@
1
+ require "securerandom"
2
+
3
+ require_relative "create_token"
4
+
5
+ module Foobara
6
+ module Auth
7
+ class CreateRefreshToken < Foobara::Command
8
+ depends_on CreateToken
9
+
10
+ inputs do
11
+ user Types::User
12
+ token_ttl :integer, default: 30 * 60
13
+ end
14
+
15
+ result :string, :sensitive_exposed
16
+
17
+ def execute
18
+ determine_timestamps
19
+ determine_token_group
20
+
21
+ generate_new_refresh_token
22
+ save_new_refresh_token_on_user
23
+
24
+ refresh_token_secret
25
+ end
26
+
27
+ attr_accessor :expires_at, :refresh_token_record, :token_group, :refresh_token_secret
28
+
29
+ def determine_timestamps
30
+ now = Time.now
31
+ self.expires_at = now + token_ttl
32
+ end
33
+
34
+ def determine_token_group
35
+ self.token_group = refresh_token_record&.token_group || SecureRandom.uuid
36
+ end
37
+
38
+ def generate_new_refresh_token
39
+ result = run_subcommand!(CreateToken, expires_at:, token_group:)
40
+
41
+ self.refresh_token_record = result[:token_record]
42
+ self.refresh_token_secret = result[:token_string]
43
+ end
44
+
45
+ def save_new_refresh_token_on_user
46
+ # TODO: maybe override #<< on these objects to dirty the entity??
47
+ user.refresh_tokens = [refresh_token_record, *user.refresh_tokens]
48
+ end
49
+ end
50
+ end
51
+ end
data/src/create_user.rb CHANGED
@@ -4,6 +4,7 @@ module Foobara
4
4
  inputs Types::User.attributes_for_create
5
5
 
6
6
  add_inputs do
7
+ user_id :integer, :allow_nil
7
8
  plaintext_password :string, :sensitive_exposed
8
9
  end
9
10
 
@@ -24,7 +25,13 @@ module Foobara
24
25
  attr_accessor :user
25
26
 
26
27
  def create_user
27
- self.user = Types::User.create(inputs.except(:plaintext_password))
28
+ inputs = self.inputs.except(:user_id, :plaintext_password)
29
+
30
+ if user_id
31
+ inputs[:id] = user_id
32
+ end
33
+
34
+ self.user = Types::User.create(inputs)
28
35
  end
29
36
 
30
37
  def password_present?
data/src/login.rb CHANGED
@@ -1,10 +1,3 @@
1
- require "jwt"
2
- require "securerandom"
3
-
4
- require_relative "create_token"
5
- require_relative "verify_password"
6
- require_relative "verify_token"
7
-
8
1
  module Foobara
9
2
  module Auth
10
3
  class Login < Foobara::Command
@@ -18,7 +11,7 @@ module Foobara
18
11
  message "No user id, email, or username given"
19
12
  end
20
13
 
21
- depends_on CreateToken, VerifyPassword, FindUser
14
+ depends_on VerifyPassword, FindUser, BuildAccessToken, CreateRefreshToken
22
15
 
23
16
  inputs do
24
17
  user Types::User, :allow_nil
@@ -39,17 +32,13 @@ module Foobara
39
32
  def execute
40
33
  find_user_to_login
41
34
  verify_password
42
- # TODO: DRY these 5 up
43
- determine_timestamps
44
35
  generate_access_token
45
- determine_token_group
46
36
  generate_new_refresh_token
47
- save_new_refresh_token_on_user
48
37
 
49
38
  tokens
50
39
  end
51
40
 
52
- attr_accessor :access_token, :new_refresh_token, :now, :expires_at, :token_group, :user_to_login
41
+ attr_accessor :access_token, :refresh_token_text, :user_to_login
53
42
 
54
43
  def find_user_to_login
55
44
  if user
@@ -83,46 +72,18 @@ module Foobara
83
72
  end
84
73
  end
85
74
 
86
- def determine_timestamps
87
- self.now = Time.now
88
- self.expires_at = now + token_ttl
89
- end
90
-
91
75
  def generate_access_token
92
- payload = { sub: user_to_login.id, exp: expires_at.to_i }
93
-
94
- self.access_token = JWT.encode(payload, jwt_secret, "HS256")
95
- end
96
-
97
- def jwt_secret
98
- jwt_secret_text = ENV.fetch("JWT_SECRET", nil)
99
-
100
- unless jwt_secret_text
101
- # :nocov:
102
- raise "You must set the JWT_SECRET environment variable"
103
- # :nocov:
104
- end
105
-
106
- jwt_secret_text
107
- end
108
-
109
- def determine_token_group
110
- self.token_group = SecureRandom.uuid
76
+ self.access_token = run_subcommand!(BuildAccessToken, user: user_to_login)
111
77
  end
112
78
 
113
79
  def generate_new_refresh_token
114
- self.new_refresh_token = run_subcommand!(CreateToken, expires_at:, token_group:)
115
- end
116
-
117
- def save_new_refresh_token_on_user
118
- # TODO: maybe override #<< on these objects to dirty the entity??
119
- user_to_login.refresh_tokens = [new_refresh_token[:token_record], *user_to_login.refresh_tokens]
80
+ self.refresh_token_text = run_subcommand!(CreateRefreshToken, user: user_to_login)
120
81
  end
121
82
 
122
83
  def tokens
123
84
  {
124
85
  access_token:,
125
- refresh_token: new_refresh_token[:token_string]
86
+ refresh_token: refresh_token_text
126
87
  }
127
88
  end
128
89
  end
data/src/logout.rb ADDED
@@ -0,0 +1,61 @@
1
+ module Foobara
2
+ module Auth
3
+ class Logout < Foobara::Command
4
+ class InvalidRefreshTokenError < Foobara::RuntimeError
5
+ context refresh_token_id: :string
6
+ message "Invalid refresh token"
7
+ end
8
+
9
+ depends_on VerifyToken
10
+ depends_on_entity Types::Token
11
+ depends_on_entity Types::User
12
+
13
+ inputs do
14
+ refresh_token :string, :allow_nil
15
+ end
16
+
17
+ # TODO: support nil result type in typescript remote command generator
18
+ result :duck
19
+
20
+ def execute
21
+ if refresh_token?
22
+ determine_refresh_token_id_and_secret
23
+ load_refresh_token_record
24
+ verify_refresh_token
25
+ # Delete it instead maybe?
26
+ mark_refresh_token_as_used
27
+ end
28
+
29
+ nil
30
+ end
31
+
32
+ attr_accessor :refresh_token_record, :refresh_token_id, :refresh_token_secret
33
+
34
+ def refresh_token?
35
+ !!refresh_token
36
+ end
37
+
38
+ def determine_refresh_token_id_and_secret
39
+ self.refresh_token_id, self.refresh_token_secret = refresh_token.split("_")
40
+ end
41
+
42
+ def load_refresh_token_record
43
+ self.refresh_token_record = Types::Token.load(refresh_token_id)
44
+ end
45
+
46
+ def verify_refresh_token
47
+ valid = run_subcommand!(VerifyToken, token_string: refresh_token)
48
+
49
+ unless valid[:verified]
50
+ # :nocov:
51
+ add_runtime_error(InvalidRefreshTokenError.new(context: { refresh_token_id: }))
52
+ # :nocov:
53
+ end
54
+ end
55
+
56
+ def mark_refresh_token_as_used
57
+ refresh_token_record.use_up!
58
+ end
59
+ end
60
+ end
61
+ end
data/src/refresh_login.rb CHANGED
@@ -1,9 +1,3 @@
1
- require "jwt"
2
- require "securerandom"
3
-
4
- require_relative "create_token"
5
- require_relative "verify_token"
6
-
7
1
  module Foobara
8
2
  module Auth
9
3
  class RefreshLogin < Foobara::Command
@@ -17,13 +11,12 @@ module Foobara
17
11
  message "This refresh token is not owned by this user"
18
12
  end
19
13
 
20
- depends_on CreateToken, VerifyToken
14
+ depends_on CreateRefreshToken, VerifyToken, BuildAccessToken
21
15
  depends_on_entities Types::Token
22
16
 
23
17
  inputs do
24
18
  refresh_token :string, :required, :sensitive
25
- # Can we get these TTLs off of the refresh token?
26
- token_ttl :integer, default: 30 * 60
19
+ access_token_ttl :integer, default: 30 * 60
27
20
  refresh_token_ttl :integer, default: 7 * 24 * 60 * 60
28
21
  end
29
22
 
@@ -38,18 +31,15 @@ module Foobara
38
31
  verify_refresh_token
39
32
  # Delete it instead maybe?
40
33
  mark_refresh_token_as_used
41
- determine_timestamps
42
- determine_token_group
43
34
 
44
35
  generate_access_token
45
36
  generate_new_refresh_token
46
- save_new_refresh_token_on_user
47
37
 
48
38
  tokens
49
39
  end
50
40
 
51
- attr_accessor :access_token, :new_refresh_token, :now, :expires_at, :refresh_token_record,
52
- :refresh_token_id, :refresh_token_secret, :token_group
41
+ attr_accessor :access_token, :new_refresh_token, :expires_at, :refresh_token_record,
42
+ :refresh_token_id, :refresh_token_secret
53
43
 
54
44
  def determine_refresh_token_id_and_secret
55
45
  self.refresh_token_id, self.refresh_token_secret = refresh_token.split("_")
@@ -71,55 +61,22 @@ module Foobara
71
61
  refresh_token_record.use_up!
72
62
  end
73
63
 
74
- def determine_timestamps
75
- self.now = Time.now
76
- self.expires_at = now + token_ttl
77
- end
78
-
79
64
  def generate_access_token
80
- payload = {
81
- user_id: user.id,
82
- username: user.username,
83
- exp: expires_at.to_i
84
- }
85
-
86
- self.access_token = JWT.encode(payload, jwt_secret, "HS256")
65
+ self.access_token = run_subcommand!(BuildAccessToken, user:, token_ttl: access_token_ttl)
87
66
  end
88
67
 
89
68
  def user
90
69
  @user ||= Types::User.that_owns(refresh_token_record, "refresh_tokens")
91
70
  end
92
71
 
93
- def jwt_secret
94
- jwt_secret_text = ENV.fetch("JWT_SECRET", nil)
95
-
96
- unless jwt_secret_text
97
- # :nocov:
98
- raise "You must set the JWT_SECRET environment variable"
99
- # :nocov:
100
- end
101
-
102
- jwt_secret_text
103
- end
104
-
105
- def determine_token_group
106
- self.token_group = refresh_token_record&.token_group || SecureRandom.uuid
107
- end
108
-
109
72
  def generate_new_refresh_token
110
- self.new_refresh_token = run_subcommand!(CreateToken, expires_at:, token_group:)
111
- end
112
-
113
- def save_new_refresh_token_on_user
114
- # TODO: maybe override #<< on these objects to dirty the entity??
115
- # TODO: DRY this up!!
116
- user.refresh_tokens = [new_refresh_token[:token_record], *user.refresh_tokens]
73
+ self.new_refresh_token = run_subcommand!(CreateRefreshToken, user:, token_ttl: refresh_token_ttl)
117
74
  end
118
75
 
119
76
  def tokens
120
77
  {
121
78
  access_token:,
122
- refresh_token: new_refresh_token[:token_string]
79
+ refresh_token: new_refresh_token
123
80
  }
124
81
  end
125
82
  end
data/src/register.rb CHANGED
@@ -6,6 +6,7 @@ module Foobara
6
6
  depends_on CreateUser, SetPassword
7
7
 
8
8
  inputs do
9
+ user_id :integer, :allow_nil
9
10
  username :string, :required
10
11
  email :email, :allow_nil
11
12
  plaintext_password :string, :allow_nil, :sensitive_exposed
@@ -26,7 +27,13 @@ module Foobara
26
27
  attr_accessor :user
27
28
 
28
29
  def create_user
29
- self.user = run_subcommand!(CreateUser, username:, email:)
30
+ inputs = { username:, email: }
31
+
32
+ if user_id
33
+ inputs[:user_id] = user_id
34
+ end
35
+
36
+ self.user = run_subcommand!(CreateUser, inputs)
30
37
  end
31
38
 
32
39
  def password?
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foobara-auth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miles Georgi
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-03-30 00:00:00.000000000 Z
10
+ date: 2025-04-01 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: argon2
@@ -64,13 +64,16 @@ files:
64
64
  - README.md
65
65
  - lib/foobara/auth.rb
66
66
  - src/approve_token.rb
67
+ - src/build_access_token.rb
67
68
  - src/build_secret.rb
68
69
  - src/create_api_key.rb
70
+ - src/create_refresh_token.rb
69
71
  - src/create_role.rb
70
72
  - src/create_token.rb
71
73
  - src/create_user.rb
72
74
  - src/find_user.rb
73
75
  - src/login.rb
76
+ - src/logout.rb
74
77
  - src/refresh_login.rb
75
78
  - src/register.rb
76
79
  - src/set_password.rb