lex-microsoft_teams 0.5.5 → 0.5.6

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: 5dd2ecc48c7eda33072b23d32d9a08947b43d51aaeba49b6b4eb46fb410e8065
4
- data.tar.gz: ec8b561ede2e96afe790aad56ecc02c3491117ea26e3bbd2e78a480d214a58d2
3
+ metadata.gz: d414b08d6eaabf909ab4de6fe900cc879bdeda369ea386925c2c3258ed9d4a24
4
+ data.tar.gz: 8bdf0da6fb7f666eb9a4489180370e5f4e3124104e9c8736d85e9d0dbe1afbb5
5
5
  SHA512:
6
- metadata.gz: 786b4d6f2b4e3cce9fee5d61d96cb2c89f31080628d19c5e146976e9eaf3ecb6e4e8eaf76854493cfa37e6dba89721c0e6dca42e8dd657bbaac67ad84b195cf6
7
- data.tar.gz: 82fc1cf2c2f31b81e7f640b5edc00be53cc976733bb7c33f3d3a68648991ad0a6979cfb2eb43f57584c48aeb7dd90542ac787e056fc44938216b7b7b9bbed41a
6
+ metadata.gz: 02b042dea9cc6292403cc504c6bdcfde101e63c24c7120300985a0825ea3dcf11aa988705f64ad9ff5bace61513b4b621f263740f7ce9e40e858b45123017acb
7
+ data.tar.gz: 9d09c3aa2518ccc19655d864c393f01ff2a578c7085c47ff4dfa130b1fb8dcec6a6cb465b2c3ae57787617201f06f9528006f60d6c0373860e2191c5b9a3216d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.6] - 2026-03-19
4
+
5
+ ### Added
6
+ - BrowserAuth API hook detection: uses hook URL when Legion::API is running instead of ephemeral CallbackServer
7
+ - `api_hook_available?` and `hook_redirect_uri` methods on BrowserAuth
8
+ - `authenticate_via_hook` path using `Legion::Events` for callback notification
9
+ - `authenticate_via_server` extracted from original `authenticate_browser` as fallback path
10
+
11
+ ### Changed
12
+ - `authenticate_browser` now delegates to hook path (API running) or server path (standalone)
13
+
3
14
  ## [0.5.5] - 2026-03-19
4
15
 
5
16
  ### Added
data/CLAUDE.md CHANGED
@@ -10,14 +10,14 @@ Legion Extension that connects LegionIO to Microsoft Teams via Graph API and Bot
10
10
 
11
11
  **GitHub**: https://github.com/LegionIO/lex-microsoft_teams
12
12
  **License**: MIT
13
- **Version**: 0.5.4
13
+ **Version**: 0.5.5
14
14
 
15
15
  ## Architecture
16
16
 
17
17
  ```
18
18
  Legion::Extensions::MicrosoftTeams
19
19
  ├── Runners/
20
- │ ├── Auth # OAuth2 client credentials (Graph + Bot Framework)
20
+ │ ├── Auth # OAuth2 client credentials (Graph + Bot Framework) + auth_callback for hook
21
21
  │ ├── Teams # List/get teams, members
22
22
  │ ├── Chats # 1:1 and group chat CRUD
23
23
  │ ├── Messages # Chat message send/read/reply
@@ -56,6 +56,8 @@ Legion::Extensions::MicrosoftTeams
56
56
  │ ├── SubscriptionRegistry # Conversation observation subscriptions (in-memory + lex-memory)
57
57
  │ ├── BrowserAuth # Delegated OAuth orchestrator (PKCE, headless detection, browser launch)
58
58
  │ └── CallbackServer # Ephemeral TCP server for OAuth redirect callback
59
+ ├── Hooks/
60
+ │ └── Auth # OAuth callback hook (mount '/callback') → /api/hooks/lex/microsoft_teams/auth/callback
59
61
  └── Client # Standalone client (includes all runners)
60
62
  ```
61
63
 
@@ -66,9 +68,9 @@ Opt-in browser-based OAuth for delegated Microsoft Graph permissions. Two flows:
66
68
  - **Authorization Code + PKCE** (primary): Opens browser for Entra ID login, captures callback on ephemeral local port, exchanges code with PKCE verification
67
69
  - **Device Code** (fallback): Auto-selected in headless/SSH environments (no `DISPLAY`/`WAYLAND_DISPLAY`)
68
70
 
69
- Tokens stored in Vault (`legionio/microsoft_teams/delegated_token`) with configurable pre-expiry silent refresh. CLI command: `legion auth teams`. Sinatra route: `GET /api/oauth/microsoft_teams/callback` for daemon re-auth.
71
+ Tokens stored in Vault (`legionio/microsoft_teams/delegated_token`) with configurable pre-expiry silent refresh. CLI command: `legion auth teams`. Hook route: `GET|POST /api/hooks/lex/microsoft_teams/auth/callback` for daemon re-auth (routed through Ingress for RBAC/audit).
70
72
 
71
- Key files: `Helpers::BrowserAuth` (orchestrator), `Helpers::CallbackServer` (ephemeral TCP), `Runners::Auth` (authorize_url, exchange_code, refresh_delegated_token), `Helpers::TokenCache` (delegated slot).
73
+ Key files: `Helpers::BrowserAuth` (orchestrator), `Helpers::CallbackServer` (ephemeral TCP), `Runners::Auth` (authorize_url, exchange_code, refresh_delegated_token, auth_callback), `Helpers::TokenCache` (delegated slot), `Hooks::Auth` (hook class with mount path).
72
74
 
73
75
  ## Token Lifecycle (v0.5.4)
74
76
 
@@ -213,7 +215,7 @@ Optional framework dependencies (guarded with `defined?`, not in gemspec):
213
215
 
214
216
  ```bash
215
217
  bundle install
216
- bundle exec rspec # 209 specs (as of v0.5.4)
218
+ bundle exec rspec # 219 specs (as of v0.5.5)
217
219
  bundle exec rubocop # Clean
218
220
  ```
219
221
 
@@ -32,6 +32,19 @@ module Legion
32
32
  end
33
33
  end
34
34
 
35
+ def api_hook_available?
36
+ !!(defined?(Legion::API) && defined?(Legion::Events))
37
+ end
38
+
39
+ def hook_redirect_uri
40
+ port = if defined?(Legion::Settings)
41
+ Legion::Settings.dig(:api, :port) || 4567
42
+ else
43
+ 4567
44
+ end
45
+ "http://127.0.0.1:#{port}/api/hooks/lex/microsoft_teams/auth/callback"
46
+ end
47
+
35
48
  def generate_pkce
36
49
  verifier = SecureRandom.urlsafe_base64(32)
37
50
  challenge = Base64.urlsafe_encode64(Digest::SHA256.digest(verifier), padding: false)
@@ -66,17 +79,62 @@ module Legion
66
79
  verifier, challenge = generate_pkce
67
80
  state = SecureRandom.hex(32)
68
81
 
82
+ if api_hook_available?
83
+ authenticate_via_hook(verifier: verifier, challenge: challenge, state: state)
84
+ else
85
+ authenticate_via_server(verifier: verifier, challenge: challenge, state: state)
86
+ end
87
+ end
88
+
89
+ def authenticate_via_hook(verifier:, challenge:, state:)
90
+ callback_uri = hook_redirect_uri
91
+ result_holder = { result: nil }
92
+ mutex = Mutex.new
93
+ cv = ConditionVariable.new
94
+
95
+ listener = Legion::Events.once('microsoft_teams.oauth.callback') do |event|
96
+ mutex.synchronize do
97
+ result_holder[:result] = event
98
+ cv.broadcast
99
+ end
100
+ end
101
+
102
+ url = @auth.authorize_url(
103
+ tenant_id: tenant_id, client_id: client_id,
104
+ redirect_uri: callback_uri, scope: scopes,
105
+ state: state, code_challenge: challenge
106
+ )
107
+
108
+ log_info('Opening browser for authentication (using API hook)...')
109
+ unless open_browser(url)
110
+ Legion::Events.off('microsoft_teams.oauth.callback', listener)
111
+ log_info('Could not open browser. Falling back to device code flow.')
112
+ return authenticate_device_code
113
+ end
114
+
115
+ mutex.synchronize { cv.wait(mutex, 120) unless result_holder[:result] }
116
+ result = result_holder[:result]
117
+
118
+ return { error: 'timeout', description: 'No callback received within timeout' } unless result && result[:code]
119
+
120
+ return { error: 'state_mismatch', description: 'CSRF state parameter mismatch' } unless result[:state] == state
121
+
122
+ @auth.exchange_code(
123
+ tenant_id: tenant_id, client_id: client_id,
124
+ code: result[:code], redirect_uri: callback_uri,
125
+ code_verifier: verifier, scope: scopes
126
+ )
127
+ end
128
+
129
+ def authenticate_via_server(verifier:, challenge:, state:)
69
130
  server = CallbackServer.new
70
131
  server.start
71
132
  callback_uri = server.redirect_uri
72
133
 
73
134
  url = @auth.authorize_url(
74
- tenant_id: tenant_id,
75
- client_id: client_id,
76
- redirect_uri: callback_uri,
77
- scope: scopes,
78
- state: state,
79
- code_challenge: challenge
135
+ tenant_id: tenant_id, client_id: client_id,
136
+ redirect_uri: callback_uri, scope: scopes,
137
+ state: state, code_challenge: challenge
80
138
  )
81
139
 
82
140
  log_info('Opening browser for authentication...')
@@ -92,12 +150,9 @@ module Legion
92
150
  return { error: 'state_mismatch', description: 'CSRF state parameter mismatch' } unless result[:state] == state
93
151
 
94
152
  @auth.exchange_code(
95
- tenant_id: tenant_id,
96
- client_id: client_id,
97
- code: result[:code],
98
- redirect_uri: callback_uri,
99
- code_verifier: verifier,
100
- scope: scopes
153
+ tenant_id: tenant_id, client_id: client_id,
154
+ code: result[:code], redirect_uri: callback_uri,
155
+ code_verifier: verifier, scope: scopes
101
156
  )
102
157
  ensure
103
158
  server&.shutdown
@@ -3,7 +3,7 @@
3
3
  module Legion
4
4
  module Extensions
5
5
  module MicrosoftTeams
6
- VERSION = '0.5.5'
6
+ VERSION = '0.5.6'
7
7
  end
8
8
  end
9
9
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-microsoft_teams
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.5
4
+ version: 0.5.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity