nova_auth_claims transforms provider-specific claims (OIDC userinfo, JWT
claims, SAML attributes) into actor maps that work with nova_auth's policies
and security callbacks.
A static mapping is a map from binary claim keys to atom actor keys:
Mapping = #{
~"sub" => id,
~"email" => email,
~"name" => display_name,
~"groups" => roles
},
Claims = #{
~"sub" => ~"abc123",
~"email" => ~"user@example.com",
~"name" => ~"Jane Doe",
~"groups" => [~"admins", ~"developers"]
},
Actor = nova_auth_claims:map(Mapping, Claims).
%% => #{id => ~"abc123", email => ~"user@example.com",
%% display_name => ~"Jane Doe", roles => [~"admins", ~"developers"]}Missing claims are skipped (no error, no undefined values):
nova_auth_claims:map(#{~"sub" => id, ~"phone" => phone}, #{~"sub" => ~"123"}).
%% => #{id => ~"123"}
%% phone is not in the result because the claim was missingFor complex transformations, use a {Module, Function} tuple:
-module(my_claims).
-export([map_authentik/1]).
map_authentik(Claims) ->
Groups = maps:get(~"groups", Claims, []),
Role = case lists:member(~"admins", Groups) of
true -> admin;
false -> user
end,
#{
id => maps:get(~"sub", Claims),
email => maps:get(~"email", Claims, undefined),
role => Role,
groups => Groups
}.Use it in your config:
%% In nova_auth_oidc config
claims_mapping => {my_claims, map_authentik}nova_auth_claims:map/3 merges mapped claims into an existing map:
Base = #{provider => authentik},
Mapping = #{~"sub" => id, ~"email" => email},
Claims = #{~"sub" => ~"abc123", ~"email" => ~"user@example.com"},
nova_auth_claims:map(Mapping, Claims, Base).
%% => #{provider => authentik, id => ~"abc123", email => ~"user@example.com"}New keys overwrite existing ones in the base map.
#{
~"sub" => id,
~"email" => email,
~"name" => display_name,
~"preferred_username" => username,
~"groups" => roles
}#{
~"sub" => id,
~"email" => email,
~"name" => display_name,
~"picture" => avatar_url
}#{
~"sub" => id,
~"email" => email,
~"preferred_username" => username,
~"realm_access" => realm_access
}For Keycloak's nested realm_access.roles, use a callback mapping to extract
the roles list.