Skip to content

Commit df673f2

Browse files
Add token helper functions
Such as those to decode and extract the expiration time
1 parent 3fd1d0a commit df673f2

File tree

4 files changed

+198
-4
lines changed

4 files changed

+198
-4
lines changed

deps/oauth2_client/BUILD.bazel

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ rabbitmq_app(
7070
deps = [
7171
"//deps/rabbit:erlang_app",
7272
"//deps/rabbit_common:erlang_app",
73+
"@jose//:erlang_app",
7374
],
7475
)
7576

@@ -94,7 +95,9 @@ dialyze(
9495

9596
eunit(
9697
name = "eunit",
97-
compiled_suites = [":test_oauth_http_mock_beam"],
98+
compiled_suites = [
99+
":test_oauth_http_mock_beam",
100+
":test_oauth2_client_test_util_beam"],
98101
target = ":test_erlang_app",
99102
)
100103

@@ -128,6 +131,9 @@ rabbitmq_integration_suite(
128131
rabbitmq_suite(
129132
name = "unit_SUITE",
130133
size = "small",
134+
additional_beam = [
135+
"test/oauth2_client_test_util.beam",
136+
],
131137
)
132138

133139
assert_suites()

deps/oauth2_client/app.bzl

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,15 @@ def all_beam_files(name = "all_beam_files"):
88
)
99
erlang_bytecode(
1010
name = "other_beam",
11-
srcs = ["src/oauth2_client.erl"],
11+
srcs = ["src/oauth2_client.erl",
12+
"src/jwt_helper.erl"],
1213
hdrs = [":public_and_private_hdrs"],
1314
app_name = "oauth2_client",
1415
dest = "ebin",
1516
erlc_opts = "//:erlc_opts",
17+
deps = [
18+
"@jose//:erlang_app"
19+
],
1620
)
1721

1822
def all_test_beam_files(name = "all_test_beam_files"):
@@ -24,11 +28,15 @@ def all_test_beam_files(name = "all_test_beam_files"):
2428
erlang_bytecode(
2529
name = "test_other_beam",
2630
testonly = True,
27-
srcs = ["src/oauth2_client.erl"],
31+
srcs = ["src/oauth2_client.erl",
32+
"src/jwt_helper.erl"],
2833
hdrs = [":public_and_private_hdrs"],
2934
app_name = "oauth2_client",
3035
dest = "test",
3136
erlc_opts = "//:test_erlc_opts",
37+
deps = [
38+
"@jose//:erlang_app"
39+
],
3240
)
3341

3442
def all_srcs(name = "all_srcs"):
@@ -46,7 +54,8 @@ def all_srcs(name = "all_srcs"):
4654

4755
filegroup(
4856
name = "srcs",
49-
srcs = ["src/oauth2_client.erl"],
57+
srcs = ["src/oauth2_client.erl",
58+
"src/jwt_helper.erl"],
5059
)
5160
filegroup(
5261
name = "private_hdrs",
@@ -90,3 +99,11 @@ def test_suite_beam_files(name = "test_suite_beam_files"):
9099
app_name = "oauth2_client",
91100
erlc_opts = "//:test_erlc_opts",
92101
)
102+
erlang_bytecode(
103+
name = "test_oauth2_client_test_util_beam",
104+
testonly = True,
105+
srcs = ["test/oauth2_client_test_util.erl"],
106+
outs = ["test/oauth2_client_test_util.beam"],
107+
app_name = "oauth2_client",
108+
erlc_opts = "//:test_erlc_opts",
109+
)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
%% This Source Code Form is subject to the terms of the Mozilla Public
2+
%% License, v. 2.0. If a copy of the MPL was not distributed with this
3+
%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
%%
5+
%% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved.
6+
%%
7+
-module(jwt_helper).
8+
9+
-export([decode/1, get_expiration/1]).
10+
11+
-include_lib("jose/include/jose_jwt.hrl").
12+
13+
decode(Token) ->
14+
try
15+
#jose_jwt{fields = Fields} = jose_jwt:peek_payload(Token),
16+
Fields
17+
catch Type:Err:Stacktrace ->
18+
{error, {invalid_token, Type, Err, Stacktrace}}
19+
end.
20+
21+
get_expiration(#{<<"exp">> := Exp}) when is_integer(Exp) -> {ok, Exp};
22+
get_expiration(#{}) -> {error, missing_exp_field}.
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
%% This Source Code Form is subject to the terms of the Mozilla Public
2+
%% License, v. 2.0. If a copy of the MPL was not distributed with this
3+
%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
%%
5+
%% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved.
6+
%%
7+
-module(oauth2_client_test_util).
8+
9+
-compile(export_all).
10+
11+
-define(DEFAULT_EXPIRATION_IN_SECONDS, 2).
12+
13+
%%
14+
%% API
15+
%%
16+
17+
sign_token_hs(Token, #{<<"kid">> := TokenKey} = Jwk) ->
18+
sign_token_hs(Token, Jwk, TokenKey).
19+
20+
sign_token_hs(Token, Jwk, TokenKey) ->
21+
Jws = #{
22+
<<"alg">> => <<"HS256">>,
23+
<<"kid">> => TokenKey
24+
},
25+
sign_token(Token, Jwk, Jws).
26+
27+
sign_token_rsa(Token, Jwk, TokenKey) ->
28+
Jws = #{
29+
<<"alg">> => <<"RS256">>,
30+
<<"kid">> => TokenKey
31+
},
32+
sign_token(Token, Jwk, Jws).
33+
34+
sign_token_no_kid(Token, Jwk) ->
35+
Signed = jose_jwt:sign(Jwk, Token),
36+
jose_jws:compact(Signed).
37+
38+
sign_token(Token, Jwk, Jws) ->
39+
Signed = jose_jwt:sign(Jwk, Jws, Token),
40+
jose_jws:compact(Signed).
41+
42+
fixture_jwk() ->
43+
fixture_jwk(<<"token-key">>).
44+
45+
fixture_jwk(TokenKey) ->
46+
fixture_jwk(TokenKey, <<"dG9rZW5rZXk">>).
47+
48+
fixture_jwk(TokenKey, K) ->
49+
#{<<"alg">> => <<"HS256">>,
50+
<<"k">> => K,
51+
<<"kid">> => TokenKey,
52+
<<"kty">> => <<"oct">>,
53+
<<"use">> => <<"sig">>,
54+
<<"value">> => TokenKey}.
55+
56+
full_permission_scopes() ->
57+
[<<"rabbitmq.configure:*/*">>,
58+
<<"rabbitmq.write:*/*">>,
59+
<<"rabbitmq.read:*/*">>].
60+
61+
expirable_token() ->
62+
expirable_token(?DEFAULT_EXPIRATION_IN_SECONDS).
63+
64+
expirable_token(Seconds) ->
65+
TokenPayload = fixture_token(),
66+
%% expiration is a timestamp with precision in seconds
67+
TokenPayload#{<<"exp">> := os:system_time(seconds) + Seconds}.
68+
69+
expired_token() ->
70+
expired_token_with_scopes(full_permission_scopes()).
71+
72+
expired_token_with_scopes(Scopes) ->
73+
token_with_scopes_and_expiration(Scopes, seconds_in_the_past(10)).
74+
75+
fixture_token_with_scopes(Scopes) ->
76+
token_with_scopes_and_expiration(Scopes, default_expiration_moment()).
77+
78+
token_with_scopes_and_expiration(Scopes, Expiration) ->
79+
%% expiration is a timestamp with precision in seconds
80+
#{<<"exp">> => Expiration,
81+
<<"iss">> => <<"unit_test">>,
82+
<<"foo">> => <<"bar">>,
83+
<<"aud">> => [<<"rabbitmq">>],
84+
<<"scope">> => Scopes}.
85+
86+
token_without_scopes() ->
87+
%% expiration is a timestamp with precision in seconds
88+
#{
89+
<<"iss">> => <<"unit_test">>,
90+
<<"foo">> => <<"bar">>,
91+
<<"aud">> => [<<"rabbitmq">>]
92+
}.
93+
94+
fixture_token() ->
95+
fixture_token([]).
96+
97+
token_with_sub(TokenFixture, Sub) ->
98+
maps:put(<<"sub">>, Sub, TokenFixture).
99+
token_with_scopes(TokenFixture, Scopes) ->
100+
maps:put(<<"scope">>, Scopes, TokenFixture).
101+
102+
fixture_token(ExtraScopes) ->
103+
Scopes = [<<"rabbitmq.configure:vhost/foo">>,
104+
<<"rabbitmq.write:vhost/foo">>,
105+
<<"rabbitmq.read:vhost/foo">>,
106+
<<"rabbitmq.read:vhost/bar">>,
107+
<<"rabbitmq.read:vhost/bar/%23%2Ffoo">>] ++ ExtraScopes,
108+
fixture_token_with_scopes(Scopes).
109+
110+
fixture_token_with_full_permissions() ->
111+
fixture_token_with_scopes(full_permission_scopes()).
112+
113+
plain_token_without_scopes_and_aud() ->
114+
%% expiration is a timestamp with precision in seconds
115+
#{<<"exp">> => default_expiration_moment(),
116+
<<"iss">> => <<"unit_test">>,
117+
<<"foo">> => <<"bar">>}.
118+
119+
token_with_scope_alias_in_scope_field(Value) ->
120+
%% expiration is a timestamp with precision in seconds
121+
#{<<"exp">> => default_expiration_moment(),
122+
<<"iss">> => <<"unit_test">>,
123+
<<"foo">> => <<"bar">>,
124+
<<"aud">> => [<<"rabbitmq">>],
125+
<<"scope">> => Value}.
126+
127+
token_with_scope_alias_in_claim_field(Claims, Scopes) ->
128+
%% expiration is a timestamp with precision in seconds
129+
#{<<"exp">> => default_expiration_moment(),
130+
<<"iss">> => <<"unit_test">>,
131+
<<"foo">> => <<"bar">>,
132+
<<"aud">> => [<<"rabbitmq">>],
133+
<<"scope">> => Scopes,
134+
<<"claims">> => Claims}.
135+
136+
seconds_in_the_future() ->
137+
seconds_in_the_future(30).
138+
139+
seconds_in_the_future(N) ->
140+
os:system_time(seconds) + N.
141+
142+
seconds_in_the_past() ->
143+
seconds_in_the_past(10).
144+
145+
seconds_in_the_past(N) ->
146+
os:system_time(seconds) - N.
147+
148+
default_expiration_moment() ->
149+
seconds_in_the_future(30).

0 commit comments

Comments
 (0)