Files
telecom-platform/sdk/elixir/lib/telecom_sdk/auth.ex
T
nutcas3 cfdbe92477 docs: Add Elixir SDK README with comprehensive API documentation and implement modular architecture with GenServer support
- Add README.md with installation instructions, architecture overview, and API examples for all SDK modules
- Refactor TelecomSDK main module to use modular API clients (SubscribersAPI, UsageAPI, PaymentAPI, RatingPlanAPI, SystemAPI, GraphQLAPI)
- Add AuthProvider class with API key and JWT token support (generate_jwt_token, validate_jwt_token methods)
- Add HTTPClient base
2026-04-25 04:55:06 +03:00

105 lines
3.1 KiB
Elixir

defmodule TelecomSDK.AuthProvider do
@moduledoc """
Authentication provider for the Telecom SDK
"""
defstruct [:api_key, :jwt_secret, :token_cache, :token_expiry]
def new(opts \\ []) do
%__MODULE__{
api_key: Keyword.get(opts, :api_key),
jwt_secret: Keyword.get(opts, :jwt_secret),
token_cache: nil,
token_expiry: nil
}
end
def get_headers(%__MODULE__{api_key: api_key, token_cache: token_cache, token_expiry: token_expiry}) do
headers = %{
"Content-Type" => "application/json",
"User-Agent" => "Telecom-Elixir-SDK/1.0.0"
}
headers = if api_key, do: Map.put(headers, "X-API-Key", api_key), else: headers
if token_cache && token_valid?(token_expiry) do
Map.put(headers, "Authorization", "Bearer #{token_cache}")
else
headers
end
end
def generate_jwt_token(%__MODULE__{jwt_secret: jwt_secret} = auth, user_id, expiry_hours \\ 24, additional_claims \\ %{}) do
unless jwt_secret, do: raise(ArgumentError, "JWT secret not configured")
now = System.system_time(:second)
exp = now + (expiry_hours * 3600)
claims = %{
"sub" => user_id,
"exp" => exp,
"iat" => now
}
|> Map.merge(additional_claims)
header = %{"alg" => "HS256", "typ" => "JWT"}
encoded_header = base64_url_encode(header)
encoded_payload = base64_url_encode(claims)
signature = sign("#{encoded_header}.#{encoded_payload}", jwt_secret)
token = "#{encoded_header}.#{encoded_payload}.#{signature}"
%{auth | token_cache: token, token_expiry: exp}
end
def validate_jwt_token(%__MODULE__{jwt_secret: jwt_secret}, token) do
unless jwt_secret, do: raise(ArgumentError, "JWT secret not configured")
parts = String.split(token, ".")
unless length(parts) == 3, do: raise(ArgumentError, "Invalid token format")
[encoded_header, encoded_payload, signature] = parts
expected_signature = sign("#{encoded_header}.#{encoded_payload}", jwt_secret)
unless signature == expected_signature, do: raise(ArgumentError, "Invalid token signature")
case base64_url_decode(encoded_payload) do
{:ok, payload_bytes} ->
case Jason.decode(payload_bytes) do
{:ok, payload} ->
if payload["exp"] && payload["exp"] < System.system_time(:second) do
raise(ArgumentError, "Token has expired")
end
payload
{:error, _} -> raise(ArgumentError, "Failed to decode payload")
end
{:error, _} -> raise(ArgumentError, "Failed to decode payload")
end
end
def clear_token_cache(auth) do
%{auth | token_cache: nil, token_expiry: nil}
end
defp token_valid?(nil), do: false
defp token_valid?(expiry), do: System.system_time(:second) < expiry
defp base64_url_encode(data) do
json = Jason.encode!(data)
json
|> Base.url_encode64(padding: false)
end
defp base64_url_decode(data) do
# Add padding if needed
padded_data = data <> String.duplicate("=", rem(4 - rem(String.length(data), 4), 4))
Base.url_decode64(padded_data)
end
defp sign(data, secret) do
:crypto.mac(:hmac, :sha256, secret, data)
|> Base.url_encode64(padding: false)
end
end