|
| 1 | +defmodule Cadet.Auth.Providers.NusEntraIdClaimExtractor do |
| 2 | + @moduledoc """ |
| 3 | + Extracts fields from NUS' Microsoft Entra ID JWTs. |
| 4 | + """ |
| 5 | + |
| 6 | + @behaviour Cadet.Auth.Providers.OpenID.ClaimExtractor |
| 7 | + |
| 8 | + @dialyzer {:nowarn_function, get_username: 2} |
| 9 | + def get_username(claims, access_token) do |
| 10 | + # TODO: Remove the debug statement after confirming the claims structure |
| 11 | + Jason.decode!(claims) |> IO.inspect(label: "Claims in NUS Entra ID") |
| 12 | + get_userinfo(access_token, "samAccountName") |
| 13 | + end |
| 14 | + |
| 15 | + @dialyzer {:nowarn_function, get_name: 2} |
| 16 | + def get_name(claims, access_token) do |
| 17 | + # TODO: Remove the debug statement after confirming the claims structure |
| 18 | + Jason.decode!(claims) |> IO.inspect(label: "Claims in NUS Entra ID") |
| 19 | + get_userinfo(access_token, "displayName") |
| 20 | + end |
| 21 | + |
| 22 | + def get_token_type, do: "access_token" |
| 23 | + |
| 24 | + # Allowed Active Directory (AD) domains; modify as needed |
| 25 | + @allowed_domains ~w(student alum staff) |
| 26 | + |
| 27 | + defp check_allowed_domain(claims) do |
| 28 | + domain = Map.get(claims, "onPremisesExtensionAttributes")["extensionAttribute6"] |
| 29 | + domain in @allowed_domains |
| 30 | + end |
| 31 | + |
| 32 | + defp map_key_to_raw("samAccountName"), do: "onPremisesSamAccountName" |
| 33 | + defp map_key_to_raw(key), do: key |
| 34 | + |
| 35 | + defp get_userinfo(token, key) do |
| 36 | + Jason.decode!(token) |> IO.inspect(label: "Token in NUS Entra ID") |
| 37 | + headers = [{"Authorization", "Bearer #{token}"}] |
| 38 | + options = [timeout: 10_000, recv_timeout: 10_000] |
| 39 | + |
| 40 | + url = |
| 41 | + "https://graph.microsoft.com/v1.0/me?$select=onPremisesSamAccountName,onPremisesExtensionAttributes" |
| 42 | + |
| 43 | + case HTTPoison.get(url, headers, options) do |
| 44 | + {:ok, %{body: body, status_code: 200}} -> |
| 45 | + data = Jason.decode!(body) |
| 46 | + |
| 47 | + with true <- check_allowed_domain(data), |
| 48 | + mapped_key <- map_key_to_raw(key), |
| 49 | + value when not is_nil(value) <- Map.get(data, mapped_key) do |
| 50 | + value |
| 51 | + else |
| 52 | + false -> nil |
| 53 | + nil -> nil |
| 54 | + end |
| 55 | + |
| 56 | + {:ok, _} -> |
| 57 | + nil |
| 58 | + end |
| 59 | + end |
| 60 | +end |
0 commit comments