Skip to content

Commit 385715f

Browse files
authored
feat: expose verified JWT claims as peek_verified_claims conn assign (#57)
* feat: expose verified JWT claims as peek_verified_claims conn assign After verify_peek_auth succeeds, the full decoded claims map is now assigned to conn.assigns[:peek_verified_claims]. This lets downstream apps read any claim (e.g. locale) without re-verifying the token or manually decoding the JWT payload. Previously the claims were parsed and discarded after building peek_account_user, forcing apps to either re-verify or base64-decode the raw token to access additional fields. * feat: add locale field to AccountUser, extract from user JWT claims The app_registry_v2 JWT format nests user info under a "user" key including a "locale" field. AccountUser now carries locale (defaults to nil for backward compat with older tokens that omit it). * chore: update changelog for verified claims and locale changes
1 parent b3c4116 commit 385715f

5 files changed

Lines changed: 53 additions & 17 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [2026-04-27]
9+
10+
### Added
11+
12+
- `AccountUser` now includes a `locale` field (optional `String.t()`), extracted from the `"locale"` key in the JWT user claims.
13+
- `PeekAuth` plug now assigns `peek_verified_claims` on the conn — the full verified JWT claims map — giving LiveViews and controllers direct access to raw claim data without re-parsing the token.
14+
815
## [2026-03-23]
916

1017
### Added

lib/peek_app_sdk/account_user.ex

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,16 @@ defmodule PeekAppSDK.AccountUser do
22
@moduledoc """
33
When iFrames are loaded, who is the logged in user?
44
"""
5-
@fields [
6-
:email,
7-
:id,
8-
:is_peek_admin,
9-
:name,
10-
:primary_role
11-
]
12-
@enforce_keys @fields
13-
defstruct @fields
5+
@enforce_keys [:email, :id, :is_peek_admin, :name, :primary_role]
6+
defstruct [:email, :id, :is_peek_admin, :name, :primary_role, locale: nil]
147

158
@type t :: %__MODULE__{
169
email: String.t(),
1710
id: String.t(),
1811
is_peek_admin: boolean(),
1912
name: String.t(),
20-
primary_role: String.t()
13+
primary_role: String.t() | nil,
14+
locale: String.t() | nil
2115
}
2216

2317
@doc """

lib/peek_app_sdk/plugs/peek_auth.ex

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ defmodule PeekAppSDK.Plugs.PeekAuth do
5656
|> assign(:peek_install_id, install_id)
5757
|> assign(:peek_account_user, build_account_user(claims))
5858
|> assign(:peek_config_id, config_id)
59+
|> assign(:peek_verified_claims, claims)
5960

6061
_ ->
6162
conn
@@ -79,19 +80,21 @@ defmodule PeekAppSDK.Plugs.PeekAuth do
7980
end
8081

8182
defp build_account_user(%{
82-
"user" => %{
83-
"id" => current_user_id,
84-
"email" => current_user_email,
85-
"is_admin" => current_user_is_peek_admin,
86-
"name" => current_user_name
87-
}
83+
"user" =>
84+
%{
85+
"id" => current_user_id,
86+
"email" => current_user_email,
87+
"is_admin" => current_user_is_peek_admin,
88+
"name" => current_user_name
89+
} = user
8890
}) do
8991
%PeekAppSDK.AccountUser{
9092
email: current_user_email,
9193
id: current_user_id,
9294
is_peek_admin: current_user_is_peek_admin,
9395
name: current_user_name,
94-
primary_role: nil
96+
primary_role: nil,
97+
locale: user["locale"]
9598
}
9699
end
97100

test/peek_app_sdk/account_user_test.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ defmodule PeekAppSDK.AccountUserTest do
1313
assert hook.id == nil
1414
assert hook.is_peek_admin == nil
1515
assert hook.primary_role == nil
16+
assert hook.locale == nil
1617
end
1718
end
1819
end

test/peek_app_sdk/plugs/peek_auth_test.exs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ defmodule PeekAppSDK.Plugs.PeekAuthTest do
2727
assert conn.assigns.peek_install_id == install_id
2828
assert conn.assigns.peek_install_token == token
2929
assert conn.assigns.peek_config_id == nil
30+
assert is_map(conn.assigns.peek_verified_claims)
31+
assert conn.assigns.peek_verified_claims["sub"] == install_id
3032

3133
# Don't test session in tests since it's not properly initialized
3234
# and we're now handling that gracefully in the implementation
@@ -214,6 +216,35 @@ defmodule PeekAppSDK.Plugs.PeekAuthTest do
214216
assert conn.assigns.peek_account_user.is_peek_admin == true
215217
assert conn.assigns.peek_account_user.name == "Legacy User"
216218
assert conn.assigns.peek_account_user.primary_role == nil
219+
assert conn.assigns.peek_account_user.locale == nil
220+
end
221+
222+
test "extracts locale from user JWT claims into account_user" do
223+
install_id = "test_install_id"
224+
225+
config = PeekAppSDK.Config.get_config()
226+
shared_secret_key = config.peek_app_secret
227+
signer = Joken.Signer.create("HS256", shared_secret_key)
228+
229+
params = %{
230+
"iss" => "app_registry_v2",
231+
"sub" => install_id,
232+
"exp" => DateTime.utc_now() |> DateTime.add(60) |> DateTime.to_unix(),
233+
"user" => %{
234+
"email" => "user@example.com",
235+
"id" => "user123",
236+
"is_admin" => false,
237+
"name" => "Test User",
238+
"locale" => "fr"
239+
}
240+
}
241+
242+
{:ok, token, _claims} = Token.generate_and_sign(params, signer)
243+
conn = conn(:post, "/", %{"peek-auth" => token})
244+
245+
conn = PeekAuth.set_peek_install_id(conn, %{})
246+
247+
assert conn.assigns.peek_account_user.locale == "fr"
217248
end
218249

219250
test "handles non-keyword list and non-map options" do

0 commit comments

Comments
 (0)