Skip to content

Commit 8e6e580

Browse files
authored
fix: avoid matching on typ as it's optional (#1488)
1 parent e29bec0 commit 8e6e580

File tree

3 files changed

+37
-20
lines changed

3 files changed

+37
-20
lines changed

lib/realtime_web/channels/auth/jwt_verification.ex

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ defmodule RealtimeWeb.JwtVerification do
6464
end
6565
end
6666

67-
defp generate_signer(%{"typ" => "JWT", "alg" => alg, "kid" => kid}, _jwt_secret, %{
67+
defp generate_signer(%{"alg" => alg, "kid" => kid}, _jwt_secret, %{
6868
"keys" => keys
6969
})
7070
when is_binary(kid) and alg in @rs_algorithms do
@@ -76,9 +76,7 @@ defmodule RealtimeWeb.JwtVerification do
7676
end
7777
end
7878

79-
defp generate_signer(%{"typ" => "JWT", "alg" => alg, "kid" => kid}, _jwt_secret, %{
80-
"keys" => keys
81-
})
79+
defp generate_signer(%{"alg" => alg, "kid" => kid}, _jwt_secret, %{"keys" => keys})
8280
when is_binary(kid) and alg in @es_algorithms do
8381
jwk = Enum.find(keys, fn jwk -> jwk["kty"] == "EC" and jwk["kid"] == kid end)
8482

@@ -88,9 +86,7 @@ defmodule RealtimeWeb.JwtVerification do
8886
end
8987
end
9088

91-
defp generate_signer(%{"typ" => "JWT", "alg" => alg, "kid" => kid}, _jwt_secret, %{
92-
"keys" => keys
93-
})
89+
defp generate_signer(%{"alg" => alg, "kid" => kid}, _jwt_secret, %{"keys" => keys})
9490
when is_binary(kid) and alg in @ed_algorithms do
9591
jwk = Enum.find(keys, fn jwk -> jwk["kty"] == "OKP" and jwk["kid"] == kid end)
9692

@@ -103,7 +99,7 @@ defmodule RealtimeWeb.JwtVerification do
10399
# Most Supabase Auth JWTs fall in this case, as they're usually signed with
104100
# HS256, have a kid header, but there's no JWK as this is sensitive. In this
105101
# case, the jwt_secret should be used.
106-
defp generate_signer(%{"typ" => "JWT", "alg" => alg, "kid" => kid}, jwt_secret, %{
102+
defp generate_signer(%{"alg" => alg, "kid" => kid}, jwt_secret, %{
107103
"keys" => keys
108104
})
109105
when is_binary(kid) and alg in @hs_algorithms do
@@ -121,8 +117,7 @@ defmodule RealtimeWeb.JwtVerification do
121117
end
122118
end
123119

124-
defp generate_signer(%{"typ" => "JWT", "alg" => alg}, jwt_secret, _jwt_jwks)
125-
when alg in @hs_algorithms do
120+
defp generate_signer(%{"alg" => alg}, jwt_secret, _jwt_jwks) when alg in @hs_algorithms do
126121
{:ok, Joken.Signer.create(alg, jwt_secret)}
127122
end
128123

mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ defmodule Realtime.MixProject do
44
def project do
55
[
66
app: :realtime,
7-
version: "2.41.15",
7+
version: "2.41.16",
88
elixir: "~> 1.17.3",
99
elixirc_paths: elixirc_paths(Mix.env()),
1010
start_permanent: Mix.env() == :prod,

test/realtime_web/channels/auth/jwt_verification_test.exs

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,24 +53,24 @@ defmodule RealtimeWeb.JwtVerificationTest do
5353
assert {:error, _reason} = JwtVerification.verify(invalid_token, @jwt_secret, nil)
5454
end
5555

56-
test "when token header does not have typ or alg" do
56+
test "when token header does not have kid or alg" do
5757
invalid_token =
58-
Base.encode64("{\"typ\": \"JWT\"}") <>
59-
"." <> Base.encode64("{}") <> "." <> Base.encode64("<<\"sig\">>")
58+
Base.encode64(~s[{"kid": "mykid123"}]) <>
59+
"." <> Base.encode64("{}") <> "." <> Base.encode64(~s[<<"sig">>])
6060

6161
assert {:error, _reason} = JwtVerification.verify(invalid_token, @jwt_secret, nil)
6262

6363
invalid_token =
64-
Base.encode64("{\"alg\": \"HS256\"}") <>
65-
"." <> Base.encode64("{}") <> "." <> Base.encode64("<<\"sig\">>")
64+
Base.encode64(~s[{"alg": "HS256"}]) <>
65+
"." <> Base.encode64("{}") <> "." <> Base.encode64(~s[<<"sig">>])
6666

6767
assert {:error, _reason} = JwtVerification.verify(invalid_token, @jwt_secret, nil)
6868
end
6969

7070
test "when token header alg is not allowed" do
7171
invalid_token =
72-
Base.encode64("{\"typ\": \"JWT\", \"alg\": \"ZZ999\"}") <>
73-
"." <> Base.encode64("{}") <> "." <> Base.encode64("<<\"sig\">>")
72+
Base.encode64(~s[{"alg": "ZZ999"}]) <>
73+
"." <> Base.encode64("{}") <> "." <> Base.encode64(~s[<<"sig">>])
7474

7575
assert {:error, _reason} = JwtVerification.verify(invalid_token, @jwt_secret, nil)
7676
end
@@ -258,7 +258,7 @@ defmodule RealtimeWeb.JwtVerificationTest do
258258
"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImtleS1pZC0xIn0.eyJpYXQiOjE3MTIwNDk2NTcsInJvbGUiOiJhdXRoZW50aWNhdGVkIiwic3ViIjoidXNlci1pZCIsImV4cCI6MTcxMjA1MzI1N30.IIQBuEiSnZacGMqiqsrLAeRGOjIaB4F3x1gnLN5zvhFryJ-6tdgu96lFv5HUF13IL2UfHWad0OuvoDt4DEHRxw"
259259

260260
# Check that the signature is valid even though time may be off.
261-
assert JwtVerification.verify(token, @jwt_secret, jwks) != {:error, :signature_error}
261+
assert {:error, [message: _, claim: "exp", claim_val: _]} = JwtVerification.verify(token, @jwt_secret, jwks)
262262
end
263263

264264
test "using ES256 JWK with wrong signature" do
@@ -299,7 +299,29 @@ defmodule RealtimeWeb.JwtVerificationTest do
299299
"eyJhbGciOiJIUzI1NiIsImtpZCI6IjRGY0d3bEJ4a0JWMWJTWnciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2hqbmRnYWdpZGlwY3RxdXFxeXloLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiJmZjA0NjVlMy1lZjk3LTRkYjItOWE1Zi0zZDI4Y2YxODE0MmYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzUyODA4NjE4LCJpYXQiOjE3NTI4MDUwMTgsImVtYWlsIjoiY2hhdEBlZHVhcmRvLmd1cmdlbC5tZSIsInBob25lIjoiIiwiYXBwX21ldGFkYXRhIjp7InByb3ZpZGVyIjoiZW1haWwiLCJwcm92aWRlcnMiOlsiZW1haWwiXX0sInVzZXJfbWV0YWRhdGEiOnsiZW1haWwiOiJjaGF0QGVkdWFyZG8uZ3VyZ2VsLm1lIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInBob25lX3ZlcmlmaWVkIjpmYWxzZSwic3ViIjoiZmYwNDY1ZTMtZWY5Ny00ZGIyLTlhNWYtM2QyOGNmMTgxNDJmIn0sInJvbGUiOiJhdXRoZW50aWNhdGVkIiwiYWFsIjoiYWFsMSIsImFtciI6W3sibWV0aG9kIjoicGFzc3dvcmQiLCJ0aW1lc3RhbXAiOjE3NTI4MDUwMTh9XSwic2Vzc2lvbl9pZCI6IjA2MDJkYWM0LWMwMjctNGIwNi1hZDM5LTMzN2ViMTZlODdlNSIsImlzX2Fub255bW91cyI6ZmFsc2V9.SnGzRjLfHPtT64kXYEQVBLKizCl76LqEPILyAPxoDwk"
300300

301301
# Check that the signature is valid even though time may be off.
302-
assert JwtVerification.verify(token, @jwt_secret, jwks) != {:error, :signature_error}
302+
assert {:error, [message: _, claim: "exp", claim_val: _]} = JwtVerification.verify(token, @jwt_secret, jwks)
303+
end
304+
305+
test "using JWT without typ header parameter" do
306+
jwks = %{
307+
"keys" => [
308+
%{
309+
"alg" => "RS256",
310+
"e" => "AQAB",
311+
"kid" => "sso_oidc_key_pair_01K20X7J43ZV31EKZMG7X3KW7K",
312+
"kty" => "RSA",
313+
"n" =>
314+
"uyc8QVrMjJTZEZoGNIJrugvf6j3iZ2Uz2IEgFuHCeC5AbM4BwZ4V1JshcW2CRs8uAmEdnH_-jiibayGmoND7OLziZJZsLir_8_kZSc5jjJ-IVFMzB1XXKze44HB3IOpcaPW1IOx0NfSI6-xOFinX7yApTZxXxmLFMEXtOWVsY_MPMrjThfvCSqesDGLRGKCn_DA4Hhaixf4NG-etGUOiavHNpqfREM7td-9caWpIyo7acWcgfYyhkxZbuYRINje1V66HWIdzzZ8uhfLXm-85LZdaWn9J83OHh3TEQW2SIAfHSNZkwXu6h5ndd_ciWxTWuvU3yjWkS-k4C8dXB--bZQ",
315+
"use" => "sig",
316+
"x5t#S256" => "HmlJQWnNuujjOdcRTCu5amk1pttdFRMBR7Rl4zplobA"
317+
}
318+
]
319+
}
320+
321+
token =
322+
"eyJhbGciOiJSUzI1NiIsImtpZCI6InNzb19vaWRjX2tleV9wYWlyXzAxSzIwWDdKNDNaVjMxRUtaTUc3WDNLVzdLIn0.eyJyb2xlIjoiYXV0aGVudGljYXRlZCIsImlzcyI6Imh0dHBzOi8vYXBpLndvcmtvcy5jb20vdXNlcl9tYW5hZ2VtZW50L2NsaWVudF8wMUsyMFg3SkNROVg1NDVXUzgwUVpQQTNGUSIsInN1YiI6InVzZXJfMDFLMjBYR1g3SkU1M1JNVEIzRjE0SEFUNTkiLCJzaWQiOiJzZXNzaW9uXzAxSzIwWEdYQVhINjhLMkI0VjcwWko3VzlZIiwianRpIjoiMDFLMjEyUzNLMEpXQ0U3Wk1URVk0RVM4UTYiLCJleHAiOjE3NTQ1MzA5MTQsImlhdCI6MTc1NDUzMDYxNH0.kt-zlPxaLVDMU9iAWCYvi3dtm9HzYponaX5E4lsS2k5F469obf4i9FyLDeXsaPyAN9V3DaAjUwys9lDyjiPsE7y1s2tFE1g9LU4goJbwinkoklUYrTmVVZcY4lkVEQSZZGwmkwgIsqKsdwTlYMzVjejsAk25Foq-FhGdsyoju21sU5S4fZPC-n8Rl8wBwD5rOPFWVHxk3z4z37585LDHhADyq1S17WxLJobfEgK_TeAlwXa2bK14vutVPpuL-qAn8nX8b6aqBoHtvRbfqsi2s72bA1S31rTgT2hgD5hqbn_g0rVmMJXt2u1zl4XDBzUj_c3lxNncUf1MgsVOHrbYRw"
323+
324+
assert {:error, [message: _, claim: "exp", claim_val: _]} = JwtVerification.verify(token, @jwt_secret, jwks)
303325
end
304326

305327
test "using HS256 JWK with wrong signature" do

0 commit comments

Comments
 (0)