seshlock 0.1.2 → 0.2.0

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: d6cfcb3d813ef94aa1c1f92cb16f2a4957c97333a80f93db7743b5be737807a6
4
- data.tar.gz: 91c4999ee8be9ccb65c2e4fdba90a2db94eeaedcb443e5e47804fc8eb55cf867
3
+ metadata.gz: fbf274a0b946e63b4705ff7c2fe438217c107cc65303f3e68af2f815d4e66b88
4
+ data.tar.gz: 301a0d5e8cbd576b149a69b99907d4d212ab0999e91e9b6c73278d7382f235c1
5
5
  SHA512:
6
- metadata.gz: 94416fa5470e6684db3943fd053a6f89b907087bb44722b8a620a1f944d48ed0027e08a6e43ae39939f91a4bbbf709a64204aeda6601bad43bc98181ee515d07
7
- data.tar.gz: d076424c00799b4e8d862ea252754bbf238c3d0e18a38206db60f96044e363529dd8b465b8a1b3d05d47832d4d607a9368a0f8eea8563ff0e88ab5073c497e6c
6
+ metadata.gz: da7ef58b5f72b5c39212117d800b286426ba1a2dd6b5ae62261d309f6dfca28c23839165714554590271f046315d297c7e14c5eefd434194e7bd049695b629f1
7
+ data.tar.gz: f6d2fadcdc3ed13121dcee13bfec93d4ec0f36d479b06648b8c0495f995befaf36f909d10f4ac53be180d541fb3acbc262d791ffcd80a78bf2a8e5615ea1aa86
data/CHANGELOG.md CHANGED
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.2.0] - 2026-01-13
9
+
10
+ ### Added
11
+
12
+ - **Session Listing**: `user.seshlock_sessions` returns all active sessions for a user, ordered by most recent first.
13
+ - **Revoke All Sessions**: `user.revoke_all_seshlock_sessions!` revokes all active sessions for a user (logout everywhere).
14
+ - **Revoke Other Sessions**: `user.revoke_other_seshlock_sessions!(except_refresh_token:)` revokes all sessions except the specified one.
15
+ - **Controller Helpers**:
16
+ - `seshlock_list_sessions` - Returns array of session info with `id`, `device`, `created_at`, `expires_at`, and `current` flag.
17
+ - `seshlock_revoke_all` - Revokes all sessions for the current user.
18
+ - `seshlock_revoke_others` - Revokes all sessions except the current one.
19
+ - `seshlock_revoke_session(session_id:)` - Revokes a specific session by ID.
20
+
8
21
  ## [0.1.2] - 2025-12-11
9
22
 
10
23
  ### Fixed
data/README.md CHANGED
@@ -160,6 +160,50 @@ The `Seshlock::UserMethods` concern adds:
160
160
  user.seshlock_refresh_tokens # => has_many association
161
161
  user.seshlock_access_tokens # => has_many through refresh_tokens
162
162
  user.issue_seshlock_session(device: nil) # => TokenPair
163
+
164
+ # Session Management
165
+ user.seshlock_sessions # => Active sessions (most recent first)
166
+ user.revoke_all_seshlock_sessions! # => Logout everywhere
167
+ user.revoke_other_seshlock_sessions!(except_refresh_token: token) # => Logout other devices
168
+ ```
169
+
170
+ ### Controller Session Management
171
+
172
+ List, revoke specific, or revoke multiple sessions:
173
+
174
+ ```ruby
175
+ class SessionsController < ApplicationController
176
+ include Seshlock::ControllerMethods
177
+
178
+ before_action :authenticate_with_seshlock_access_token!,
179
+ only: [:index, :destroy, :destroy_others]
180
+ before_action :authenticate_with_seshlock_refresh_token!,
181
+ only: [:destroy_all]
182
+
183
+ # GET /sessions - List all active sessions
184
+ def index
185
+ render json: { sessions: seshlock_list_sessions }
186
+ # Returns: [{ id: 1, device: "iPhone", created_at: ..., expires_at: ..., current: true }, ...]
187
+ end
188
+
189
+ # DELETE /sessions/:id - Revoke specific session
190
+ def destroy
191
+ seshlock_revoke_session(session_id: params[:id])
192
+ head :ok
193
+ end
194
+
195
+ # DELETE /sessions/others - Revoke all except current
196
+ def destroy_others
197
+ seshlock_revoke_others
198
+ head :ok
199
+ end
200
+
201
+ # DELETE /sessions/all - Revoke all (requires refresh token)
202
+ def destroy_all
203
+ seshlock_revoke_all
204
+ head :ok
205
+ end
206
+ end
163
207
  ```
164
208
 
165
209
  ## Error Handling
@@ -8,6 +8,7 @@ class Seshlock::AccessToken < ActiveRecord::Base
8
8
 
9
9
  # Associations
10
10
  belongs_to :refresh_token, class_name: "Seshlock::RefreshToken", inverse_of: :access_tokens
11
+ has_one :user, through: :refresh_token
11
12
 
12
13
  # Scopes
13
14
  scope :not_expired, -> { where("expires_at > ?", Time.current) }
@@ -153,6 +153,60 @@ module Seshlock
153
153
  build_token_response(user: user, token_pair: token_pair)
154
154
  end
155
155
 
156
+ # -------------------------------------------------------------------------
157
+ # Session Management Helpers
158
+ # -------------------------------------------------------------------------
159
+
160
+ # Lists all active sessions for the current user.
161
+ #
162
+ # Call authenticate_with_seshlock_access_token! before this method.
163
+ #
164
+ # @return [Array<Hash>] session info for each active refresh token
165
+ def seshlock_list_sessions
166
+ @current_seshlock_user.seshlock_sessions.map do |refresh_token|
167
+ {
168
+ id: refresh_token.id,
169
+ device: refresh_token.device_identifier,
170
+ created_at: refresh_token.created_at,
171
+ expires_at: refresh_token.expires_at,
172
+ current: refresh_token.id == @current_seshlock_access_token&.refresh_token_id
173
+ }
174
+ end
175
+ end
176
+
177
+ # Revokes all sessions for the current user.
178
+ #
179
+ # Call authenticate_with_seshlock_refresh_token! before this method.
180
+ # This will log the user out of all devices including the current one.
181
+ #
182
+ # @return [void]
183
+ def seshlock_revoke_all
184
+ @current_seshlock_refresh_token.user.revoke_all_seshlock_sessions!
185
+ end
186
+
187
+ # Revokes all sessions except the current one.
188
+ #
189
+ # Call authenticate_with_seshlock_access_token! before this method.
190
+ #
191
+ # @return [void]
192
+ def seshlock_revoke_others
193
+ @current_seshlock_user.revoke_other_seshlock_sessions!(
194
+ except_refresh_token: @current_seshlock_access_token.refresh_token
195
+ )
196
+ end
197
+
198
+ # Revokes a specific session by ID.
199
+ #
200
+ # Call authenticate_with_seshlock_access_token! before this method.
201
+ #
202
+ # @param session_id [Integer] the refresh token ID to revoke
203
+ # @return [void]
204
+ # @raise [ActiveRecord::RecordNotFound] if session doesn't belong to user
205
+ def seshlock_revoke_session(session_id:)
206
+ refresh_token = @current_seshlock_user.seshlock_refresh_tokens.find(session_id)
207
+ refresh_token.revoke!
208
+ end
209
+
156
210
  private
157
211
 
158
212
  # Extracts the raw refresh token from params.
@@ -18,9 +18,37 @@ module Seshlock
18
18
  through: :seshlock_refresh_tokens
19
19
  end
20
20
 
21
- # Convenience helper
21
+ # Issue a new session for this user.
22
+ #
23
+ # @param device [String, nil] optional device identifier
24
+ # @return [Seshlock::TokenPair] the issued token pair
22
25
  def issue_seshlock_session(device: nil)
23
26
  Seshlock::Sessions.issue_tokens_to(user: self, device: device)
24
27
  end
28
+
29
+ # List all active sessions for this user.
30
+ #
31
+ # @return [ActiveRecord::Relation<Seshlock::RefreshToken>] active refresh tokens
32
+ def seshlock_sessions
33
+ seshlock_refresh_tokens.active.order(created_at: :desc)
34
+ end
35
+
36
+ # Revoke all sessions for this user.
37
+ #
38
+ # @return [void]
39
+ def revoke_all_seshlock_sessions!
40
+ seshlock_refresh_tokens.active.find_each(&:revoke!)
41
+ end
42
+
43
+ # Revoke all sessions except the specified one.
44
+ #
45
+ # @param except_refresh_token [Seshlock::RefreshToken] the session to keep active
46
+ # @return [void]
47
+ def revoke_other_seshlock_sessions!(except_refresh_token:)
48
+ seshlock_refresh_tokens
49
+ .active
50
+ .where.not(id: except_refresh_token.id)
51
+ .find_each(&:revoke!)
52
+ end
25
53
  end
26
54
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Seshlock
4
- VERSION = "0.1.2"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: seshlock
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Inoa
@@ -67,7 +67,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  requirements: []
70
- rubygems_version: 3.6.9
70
+ rubygems_version: 4.0.3
71
71
  specification_version: 4
72
72
  summary: Session tokens (access + refresh) for Rails apps.
73
73
  test_files: []