feat(jwt authn): allow to specify JWT field
This commit is contained in:
parent
2ad2da082b
commit
d0f686d19d
|
@ -217,8 +217,21 @@ Authentication will verify that the value of claims in the JWT (taken from the P
|
|||
zh: """JWT claim name to use for getting ACL rules."""
|
||||
}
|
||||
label {
|
||||
en: """acl_claim_name"""
|
||||
zh: """acl_claim_name"""
|
||||
en: """ACL claim name"""
|
||||
zh: """ACL claim name"""
|
||||
}
|
||||
}
|
||||
|
||||
from {
|
||||
desc {
|
||||
en: """Field to take JWT from."""
|
||||
zh: """要从中获取 JWT 的字段。"""
|
||||
}
|
||||
label {
|
||||
en: """From Field"""
|
||||
zh: """源字段"""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -117,7 +117,8 @@ common_fields() ->
|
|||
default => <<"acl">>,
|
||||
desc => ?DESC(acl_claim_name)
|
||||
}},
|
||||
{verify_claims, fun verify_claims/1}
|
||||
{verify_claims, fun verify_claims/1},
|
||||
{from, fun from/1}
|
||||
] ++ emqx_authn_schema:common_fields().
|
||||
|
||||
secret(type) -> binary();
|
||||
|
@ -184,6 +185,11 @@ verify_claims(required) ->
|
|||
verify_claims(_) ->
|
||||
undefined.
|
||||
|
||||
from(type) -> hoconsc:enum([username, password]);
|
||||
from(desc) -> ?DESC(?FUNCTION_NAME);
|
||||
from(default) -> password;
|
||||
from(_) -> undefined.
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% APIs
|
||||
%%------------------------------------------------------------------------------
|
||||
|
@ -234,22 +240,25 @@ update(#{use_jwks := true} = Config, _State) ->
|
|||
authenticate(#{auth_method := _}, _) ->
|
||||
ignore;
|
||||
authenticate(
|
||||
Credential = #{password := JWT},
|
||||
Credential,
|
||||
#{
|
||||
verify_claims := VerifyClaims0,
|
||||
jwk := JWK,
|
||||
acl_claim_name := AclClaimName
|
||||
acl_claim_name := AclClaimName,
|
||||
from := From
|
||||
}
|
||||
) ->
|
||||
JWT = maps:get(From, Credential),
|
||||
JWKs = [JWK],
|
||||
VerifyClaims = replace_placeholder(VerifyClaims0, Credential),
|
||||
verify(JWT, JWKs, VerifyClaims, AclClaimName);
|
||||
authenticate(
|
||||
Credential = #{password := JWT},
|
||||
Credential,
|
||||
#{
|
||||
verify_claims := VerifyClaims0,
|
||||
jwk_resource := ResourceId,
|
||||
acl_claim_name := AclClaimName
|
||||
acl_claim_name := AclClaimName,
|
||||
from := From
|
||||
}
|
||||
) ->
|
||||
case emqx_resource:query(ResourceId, get_jwks) of
|
||||
|
@ -261,6 +270,7 @@ authenticate(
|
|||
}),
|
||||
ignore;
|
||||
{ok, JWKs} ->
|
||||
JWT = maps:get(From, Credential),
|
||||
VerifyClaims = replace_placeholder(VerifyClaims0, Credential),
|
||||
verify(JWT, JWKs, VerifyClaims, AclClaimName)
|
||||
end.
|
||||
|
@ -281,7 +291,8 @@ create2(#{
|
|||
secret := Secret0,
|
||||
secret_base64_encoded := Base64Encoded,
|
||||
verify_claims := VerifyClaims,
|
||||
acl_claim_name := AclClaimName
|
||||
acl_claim_name := AclClaimName,
|
||||
from := From
|
||||
}) ->
|
||||
case may_decode_secret(Base64Encoded, Secret0) of
|
||||
{error, Reason} ->
|
||||
|
@ -291,7 +302,8 @@ create2(#{
|
|||
{ok, #{
|
||||
jwk => JWK,
|
||||
verify_claims => VerifyClaims,
|
||||
acl_claim_name => AclClaimName
|
||||
acl_claim_name => AclClaimName,
|
||||
from => From
|
||||
}}
|
||||
end;
|
||||
create2(#{
|
||||
|
@ -299,19 +311,22 @@ create2(#{
|
|||
algorithm := 'public-key',
|
||||
public_key := PublicKey,
|
||||
verify_claims := VerifyClaims,
|
||||
acl_claim_name := AclClaimName
|
||||
acl_claim_name := AclClaimName,
|
||||
from := From
|
||||
}) ->
|
||||
JWK = create_jwk_from_public_key(PublicKey),
|
||||
{ok, #{
|
||||
jwk => JWK,
|
||||
verify_claims => VerifyClaims,
|
||||
acl_claim_name => AclClaimName
|
||||
acl_claim_name => AclClaimName,
|
||||
from => From
|
||||
}};
|
||||
create2(
|
||||
#{
|
||||
use_jwks := true,
|
||||
verify_claims := VerifyClaims,
|
||||
acl_claim_name := AclClaimName
|
||||
acl_claim_name := AclClaimName,
|
||||
from := From
|
||||
} = Config
|
||||
) ->
|
||||
ResourceId = emqx_authn_utils:make_resource_id(?MODULE),
|
||||
|
@ -324,7 +339,8 @@ create2(
|
|||
{ok, #{
|
||||
jwk_resource => ResourceId,
|
||||
verify_claims => VerifyClaims,
|
||||
acl_claim_name => AclClaimName
|
||||
acl_claim_name => AclClaimName,
|
||||
from => From
|
||||
}}.
|
||||
|
||||
create_jwk_from_public_key(PublicKey) when
|
||||
|
@ -366,6 +382,8 @@ replace_placeholder([{Name, {placeholder, PL}} | More], Variables, Acc) ->
|
|||
replace_placeholder([{Name, Value} | More], Variables, Acc) ->
|
||||
replace_placeholder(More, Variables, [{Name, Value} | Acc]).
|
||||
|
||||
verify(undefined, _, _, _) ->
|
||||
ignore;
|
||||
verify(JWT, JWKs, VerifyClaims, AclClaimName) ->
|
||||
case do_verify(JWT, JWKs, VerifyClaims) of
|
||||
{ok, Extra} -> {ok, acl(Extra, AclClaimName)};
|
||||
|
|
|
@ -52,10 +52,11 @@ end_per_suite(_) ->
|
|||
%% Tests
|
||||
%%------------------------------------------------------------------------------
|
||||
|
||||
t_jwt_authenticator_hmac_based(_) ->
|
||||
t_hmac_based(_) ->
|
||||
Secret = <<"abcdef">>,
|
||||
Config = #{
|
||||
mechanism => jwt,
|
||||
from => password,
|
||||
acl_claim_name => <<"acl">>,
|
||||
use_jwks => false,
|
||||
algorithm => 'hmac-based',
|
||||
|
@ -175,11 +176,12 @@ t_jwt_authenticator_hmac_based(_) ->
|
|||
?assertEqual(ok, emqx_authn_jwt:destroy(State3)),
|
||||
ok.
|
||||
|
||||
t_jwt_authenticator_public_key(_) ->
|
||||
t_public_key(_) ->
|
||||
PublicKey = test_rsa_key(public),
|
||||
PrivateKey = test_rsa_key(private),
|
||||
Config = #{
|
||||
mechanism => jwt,
|
||||
from => password,
|
||||
acl_claim_name => <<"acl">>,
|
||||
use_jwks => false,
|
||||
algorithm => 'public-key',
|
||||
|
@ -202,6 +204,28 @@ t_jwt_authenticator_public_key(_) ->
|
|||
?assertEqual(ok, emqx_authn_jwt:destroy(State)),
|
||||
ok.
|
||||
|
||||
t_jwt_in_username(_) ->
|
||||
Secret = <<"abcdef">>,
|
||||
Config = #{
|
||||
mechanism => jwt,
|
||||
from => username,
|
||||
acl_claim_name => <<"acl">>,
|
||||
use_jwks => false,
|
||||
algorithm => 'hmac-based',
|
||||
secret => Secret,
|
||||
secret_base64_encoded => false,
|
||||
verify_claims => []
|
||||
},
|
||||
{ok, State} = emqx_authn_jwt:create(?AUTHN_ID, Config),
|
||||
|
||||
Payload = #{<<"exp">> => erlang:system_time(second) + 60},
|
||||
JWS = generate_jws('hmac-based', Payload, Secret),
|
||||
Credential = #{
|
||||
username => JWS,
|
||||
password => <<"pass">>
|
||||
},
|
||||
?assertMatch({ok, #{is_superuser := false}}, emqx_authn_jwt:authenticate(Credential, State)).
|
||||
|
||||
t_jwks_renewal(_Config) ->
|
||||
{ok, _} = emqx_authn_http_test_server:start_link(?JWKS_PORT, ?JWKS_PATH, server_ssl_opts()),
|
||||
ok = emqx_authn_http_test_server:set_handler(fun jwks_handler/2),
|
||||
|
@ -216,6 +240,7 @@ t_jwks_renewal(_Config) ->
|
|||
|
||||
BadConfig0 = #{
|
||||
mechanism => jwt,
|
||||
from => password,
|
||||
acl_claim_name => <<"acl">>,
|
||||
algorithm => 'public-key',
|
||||
ssl => #{enable => false},
|
||||
|
@ -307,10 +332,11 @@ t_jwks_renewal(_Config) ->
|
|||
?assertEqual(ok, emqx_authn_jwt:destroy(State2)),
|
||||
ok = emqx_authn_http_test_server:stop().
|
||||
|
||||
t_jwt_authenticator_verify_claims(_) ->
|
||||
t_verify_claims(_) ->
|
||||
Secret = <<"abcdef">>,
|
||||
Config0 = #{
|
||||
mechanism => jwt,
|
||||
from => password,
|
||||
acl_claim_name => <<"acl">>,
|
||||
use_jwks => false,
|
||||
algorithm => 'hmac-based',
|
||||
|
|
Loading…
Reference in New Issue