add authentication with zitadel
Some checks failed
Build and Publish / build-release (push) Failing after 1m49s

This commit is contained in:
2026-05-04 15:52:09 -05:00
parent 636d456c24
commit 44d89014fd
14 changed files with 614 additions and 12 deletions

View File

@@ -40,6 +40,18 @@ cors_origin = System.get_env("CORS_ORIGIN", "*")
config :cors_plug,
origin: cors_origin
# Zitadel Configuration
config :policy_service, :zitadel,
issuer: System.get_env("ZITADEL_ISSUER"),
client_id: System.get_env("ZITADEL_CLIENT_ID"),
client_secret: System.get_env("ZITADEL_CLIENT_SECRET"),
required_scopes: [
"openid",
"profile",
"urn:zitadel:iam:user:resourceowner",
"urn:zitadel:iam:org:projects:roles"
]
# ## Using releases
#
# If you use `mix release`, you need to explicitly enable the server

View File

@@ -21,7 +21,7 @@
mixFodDeps = beamPackages.fetchMixDeps {
inherit pname version;
src = ./.;
sha256 = "sha256-ZyrQiL5sOCC+V9S7rVGrCNEqm/TDZkjIx6Y+MZy7+6s=";
sha256 = "sha256-YqPo8102nqTd6cAxt6O2R+nLLa9UfRLza4qojxoMtqM=";
};
package = beamPackages.mixRelease {
inherit pname version mixFodDeps;

View File

@@ -32,7 +32,8 @@ defmodule PolicyService.Aggregates.LifePolicyApplication do
def validate_insured_object(_), do: {:error, :invalid_life_details}
def validate_insured(%{"type" => "corporate"}), do: {:error, :life_insurance_requires_individual}
def validate_insured(%{"type" => "corporate"}),
do: {:error, :life_insurance_requires_individual}
def validate_insured(%{"type" => "individual", "gender" => gender} = insured)
when is_binary(gender) and byte_size(gender) > 0 do

View File

@@ -5,6 +5,10 @@ defmodule PolicyService.Application do
use Application
defp get_zitadel_config(key) do
Application.get_env(:policy_service, :zitadel)[key]
end
@impl true
def start(_type, _args) do
children = [
@@ -19,6 +23,11 @@ defmodule PolicyService.Application do
PolicyService.Repo,
{DNSCluster, query: Application.get_env(:policy_service, :dns_cluster_query) || :ignore},
{Phoenix.PubSub, name: PolicyService.PubSub, pool_size: 1},
{Oidcc.ProviderConfiguration.Worker,
%{
issuer: get_zitadel_config(:issuer),
name: PolicyService.ZitadelProvider
}},
PolicyServiceWeb.Endpoint
]

View File

@@ -1,5 +1,5 @@
defmodule PolicyServiceWeb.ApiSpec do
alias OpenApiSpex.{OpenApi, Info, Server}
alias OpenApiSpex.{OpenApi, Info, Server, Components, SecurityScheme}
alias OpenApiSpex.{Info, OpenApi, Paths, Server}
alias PolicyServiceWeb.{Endpoint, Router}
@behaviour OpenApi
@@ -16,7 +16,18 @@ defmodule PolicyServiceWeb.ApiSpec do
version: "1.0"
},
# Populate the paths from a phoenix router
paths: Paths.from_router(Router)
paths: Paths.from_router(Router),
components: %Components{
securitySchemes: %{
"bearerAuth" => %SecurityScheme{
type: "http",
scheme: "bearer",
bearerFormat: "JWT",
description: "Zitadel JWT bearer token for authentication"
}
}
},
security: [%{"bearerAuth" => []}]
}
# Discover request/response schemas from path specs
|> OpenApiSpex.resolve_schema_modules()

View File

@@ -31,7 +31,7 @@ defmodule PolicyServiceWeb.PolicyController do
)
def index(conn, params) do
org_id = conn.assigns[:org_id] || "test"
org_id = conn.assigns[:org_id]
case PolicyQueries.list_by_org(org_id, params) do
{:ok, {policies, meta}} ->
@@ -63,7 +63,7 @@ defmodule PolicyServiceWeb.PolicyController do
)
def show(conn, %{"application_id" => application_id}) do
org_id = conn.assigns[:org_id] || "test"
org_id = conn.assigns[:org_id]
case PolicyQueries.get_by_application_id(org_id, application_id) do
{:ok, policy} ->
@@ -89,8 +89,8 @@ defmodule PolicyServiceWeb.PolicyController do
def create(conn, params) do
application_id = Ecto.UUID.generate()
org_id = conn.assigns[:org_id] || "test"
submitted_by = conn.assigns[:user_id] || "test"
org_id = conn.assigns[:org_id]
submitted_by = conn.assigns[:user_id]
with {:ok, policy_type} <- parse_policy_type(params["policy_type"]),
{:ok, insured} <- parse_insured(params["insured"]),
@@ -173,7 +173,7 @@ defmodule PolicyServiceWeb.PolicyController do
)
def accept(conn, %{"application_id" => application_id} = params) do
org_id = conn.assigns[:org_id] || "test"
org_id = conn.assigns[:org_id]
with {:ok, policy} <- PolicyQueries.get_by_application_id(org_id, application_id) do
command =

View File

@@ -0,0 +1,200 @@
defmodule PolicyServiceWeb.Plugs.AuthenticationPlug do
@moduledoc """
Authentication plug for validating JWT tokens using Zitadel.
This plug validates JWT tokens using the oidcc library and extracts
user claims for use throughout the application.
"""
import Plug.Conn
require Logger
@doc """
Initializes the authentication plug.
## Options
- :provider - The OIDCC provider configuration worker name (required)
- :client_id - OAuth2 client ID (required) - can be a string or {module, function, args} tuple
- :client_secret - OAuth2 client secret (required) - can be a string or {module, function, args} tuple
- :required_scopes - List of required scopes (optional)
"""
def init(opts) do
provider = Keyword.fetch!(opts, :provider)
client_id = Keyword.fetch!(opts, :client_id)
client_secret = Keyword.fetch!(opts, :client_secret)
required_scopes = Keyword.get(opts, :required_scopes, [])
%{
provider: provider,
client_id: resolve_config(client_id),
client_secret: resolve_config(client_secret),
required_scopes: required_scopes
}
end
defp resolve_config({module, function, args})
when is_atom(module) and is_atom(function) and is_list(args) do
apply(module, function, args)
end
defp resolve_config(value) when is_binary(value), do: value
defp resolve_config(value) when is_function(value, 0), do: value.()
defp resolve_config(value), do: value
@doc """
Authenticates the request by validating the JWT token.
Extracts the Authorization header, validates the JWT token using
Zitadel's JWKS, and assigns the extracted claims to the connection.
"""
def call(conn, config) do
with {:ok, token} <- extract_token(conn),
{:ok, validated_token} <- validate_token(token, config),
{:ok, claims} <- extract_claims(validated_token) do
conn
|> assign(:authenticated, true)
|> assign(:current_user, claims)
|> assign(:user_id, claims.user_id)
|> assign(:org_id, claims.org_id)
|> assign(:roles, claims.roles)
|> assign(:scopes, claims.scopes)
else
{:error, :missing_token} ->
handle_missing_token(conn)
{:error, :invalid_token} = error ->
handle_invalid_token(conn, error)
{:error, :expired_token} = error ->
handle_expired_token(conn, error)
{:error, :insufficient_scopes} = error ->
handle_insufficient_scopes(conn, error)
{:error, reason} = error ->
handle_authentication_error(conn, error, reason)
end
end
defp extract_token(conn) do
case get_req_header(conn, "authorization") do
["Bearer " <> token] when token != "" ->
{:ok, token}
["Bearer " <> token] when token == "" ->
{:error, :missing_token}
["bearer " <> token] when token != "" ->
{:ok, token}
[] ->
{:error, :missing_token}
_ ->
{:error, :invalid_token}
end
end
defp validate_token(token, config) do
try do
case Oidcc.Token.validate_jwt(
token,
config.provider,
%{}
) do
{:ok, validated_token} ->
validate_required_scopes(validated_token, config)
{:error, :invalid_token} ->
Logger.warning("Invalid JWT token provided")
{:error, :invalid_token}
{:error, :expired_token} ->
Logger.warning("Expired JWT token provided")
{:error, :expired_token}
{:error, reason} ->
Logger.error("Token validation failed: #{inspect(reason)}")
{:error, :invalid_token}
end
rescue
error ->
Logger.error("Token validation error: #{inspect(error)}")
{:error, :invalid_token}
end
end
defp validate_required_scopes(validated_token, config) do
required_scopes = config.required_scopes || []
if required_scopes == [] do
{:ok, validated_token}
else
token_scopes = PolicyServiceWeb.Plugs.ClaimsExtractor.get_scopes(validated_token.claims)
if has_all_required_scopes?(token_scopes, required_scopes) do
{:ok, validated_token}
else
missing_scopes = required_scopes -- token_scopes
Logger.warning("Token missing required scopes: #{inspect(missing_scopes)}")
{:error, :insufficient_scopes}
end
end
end
defp has_all_required_scopes?(token_scopes, required_scopes) do
Enum.all?(required_scopes, fn scope -> scope in token_scopes end)
end
defp extract_claims(validated_token) do
try do
claims = PolicyServiceWeb.Plugs.ClaimsExtractor.extract_claims(validated_token)
PolicyServiceWeb.Plugs.ClaimsExtractor.validate_claims!(validated_token.claims)
{:ok, claims}
rescue
error ->
Logger.error("Failed to extract claims: #{inspect(error)}")
{:error, :invalid_claims}
end
end
defp handle_missing_token(conn) do
conn
|> put_resp_content_type("application/json")
|> send_resp(401, Jason.encode!(%{error: "Missing authentication token"}))
|> halt()
end
defp handle_invalid_token(conn, _error) do
conn
|> put_resp_content_type("application/json")
|> send_resp(401, Jason.encode!(%{error: "Invalid authentication token"}))
|> halt()
end
defp handle_expired_token(conn, _error) do
conn
|> put_resp_content_type("application/json")
|> send_resp(401, Jason.encode!(%{error: "Authentication token has expired"}))
|> halt()
end
defp handle_insufficient_scopes(conn, _error) do
conn
|> put_resp_content_type("application/json")
|> send_resp(
403,
Jason.encode!(%{error: "Insufficient permissions - required scopes not granted"})
)
|> halt()
end
defp handle_authentication_error(conn, _error, reason) do
Logger.error("Authentication error: #{inspect(reason)}")
conn
|> put_resp_content_type("application/json")
|> send_resp(401, Jason.encode!(%{error: "Authentication failed"}))
|> halt()
end
end

View File

@@ -0,0 +1,184 @@
defmodule PolicyServiceWeb.Plugs.AuthorizationPlug do
@moduledoc """
Authorization plug for enforcing role-based access control.
This plug checks if authenticated users have the required roles
and scopes to access protected resources.
"""
import Plug.Conn
require Logger
@doc """
Initializes the authorization plug.
## Options
- :required_roles - List of roles that can access the resource (optional)
- :required_scopes - List of scopes required to access the resource (optional)
- :resource_owner_check - Function to check if user owns the resource (optional)
"""
def init(opts) do
%{
required_roles: Keyword.get(opts, :required_roles, []),
required_scopes: Keyword.get(opts, :required_scopes, []),
resource_owner_check: Keyword.get(opts, :resource_owner_check, nil)
}
end
@doc """
Authorizes the request by checking roles and scopes.
Verifies that the authenticated user has the required roles and scopes
to access the requested resource.
"""
def call(conn, config) do
user_roles = conn.assigns[:roles] || []
user_scopes = conn.assigns[:scopes] || []
user_id = conn.assigns[:user_id]
org_id = conn.assigns[:org_id]
with :ok <- check_roles(user_roles, config.required_roles),
:ok <- check_scopes(user_scopes, config.required_scopes),
:ok <- check_resource_ownership(conn, config.resource_owner_check, user_id, org_id) do
conn
else
{:error, :insufficient_role} ->
handle_insufficient_role(conn, config.required_roles)
{:error, :insufficient_scope} ->
handle_insufficient_scope(conn, config.required_scopes)
{:error, :not_owner} ->
handle_not_owner(conn)
end
end
defp check_roles(_user_roles, required_roles) when required_roles == [] do
:ok
end
defp check_roles(user_roles, required_roles) do
if has_any_role?(user_roles, required_roles) do
:ok
else
Logger.warning(
"User with roles #{inspect(user_roles)} lacks required roles: #{inspect(required_roles)}"
)
{:error, :insufficient_role}
end
end
defp check_scopes(_user_scopes, required_scopes) when required_scopes == [] do
:ok
end
defp check_scopes(user_scopes, required_scopes) do
if has_all_scopes?(user_scopes, required_scopes) do
:ok
else
Logger.warning(
"User with scopes #{inspect(user_scopes)} lacks required scopes: #{inspect(required_scopes)}"
)
{:error, :insufficient_scope}
end
end
defp check_resource_ownership(_conn, nil, _user_id, _org_id) do
:ok
end
defp check_resource_ownership(conn, check_function, user_id, org_id) do
try do
if check_function.(conn, user_id, org_id) do
:ok
else
{:error, :not_owner}
end
rescue
error ->
Logger.error("Resource ownership check failed: #{inspect(error)}")
{:error, :not_owner}
end
end
@doc """
Checks if the user has any of the required roles.
Supports role hierarchy where admin has all permissions.
"""
def has_any_role?(user_roles, required_roles) do
cond do
"admin" in user_roles ->
true
Enum.any?(required_roles, fn role -> role in user_roles end) ->
true
true ->
false
end
end
@doc """
Checks if the user has all of the required scopes.
"""
def has_all_scopes?(user_scopes, required_scopes) do
Enum.all?(required_scopes, fn scope -> scope in user_scopes end)
end
@doc """
Checks if the user can access a specific resource based on ownership.
Admins can access any resource. Other users can only access their own resources.
"""
def can_access_resource?(user_roles, resource_user_id, current_user_id) do
cond do
"admin" in user_roles ->
true
resource_user_id == current_user_id ->
true
true ->
false
end
end
defp handle_insufficient_role(conn, required_roles) do
conn
|> put_resp_content_type("application/json")
|> send_resp(
403,
Jason.encode!(%{
error: "Insufficient permissions",
required_roles: required_roles
})
)
|> halt()
end
defp handle_insufficient_scope(conn, required_scopes) do
conn
|> put_resp_content_type("application/json")
|> send_resp(
403,
Jason.encode!(%{
error: "Insufficient permissions",
required_scopes: required_scopes
})
)
|> halt()
end
defp handle_not_owner(conn) do
conn
|> put_resp_content_type("application/json")
|> send_resp(
403,
Jason.encode!(%{error: "You do not have permission to access this resource"})
)
|> halt()
end
end

View File

@@ -0,0 +1,127 @@
defmodule PolicyServiceWeb.Plugs.ClaimsExtractor do
@moduledoc """
Extracts and normalizes Zitadel claims from validated JWT tokens.
This module handles the extraction of organization ID, user ID, roles,
and scopes from Zitadel standard claims and normalizes them for use
throughout the application.
"""
@doc """
Extracts and normalizes claims from a validated OIDCC token.
## Parameters
- token: The validated OIDCC token struct
## Returns
A map containing normalized claims:
- org_id: Organization ID from Zitadel claims
- user_id: User ID from Zitadel claims
- roles: List of user roles
- scopes: List of granted scopes
"""
def extract_claims(token) do
claims = token.claims
%{
org_id: get_org_id(claims),
user_id: get_user_id(claims),
roles: get_roles(claims),
scopes: get_scopes(claims)
}
end
@doc """
Extracts organization ID from Zitadel claims.
Uses the Zitadel-specific claim 'urn:zitadel:iam:user:resourceowner:id'
or falls back to the standard 'azp' (authorized party) claim.
"""
def get_org_id(claims) do
claims["urn:zitadel:iam:user:resourceowner:id"] ||
claims["azp"] ||
"default"
end
@doc """
Extracts user ID from Zitadel claims.
Uses the standard 'sub' (subject) claim as the primary user identifier.
"""
def get_user_id(claims) do
claims["sub"] ||
raise "Missing required user_id claim"
end
@doc """
Extracts roles from Zitadel claims.
Zitadel provides roles in the 'urn:zitadel:iam:org:project:roles' claim
with a nested structure containing role keys and organization context.
"""
def get_roles(claims) do
project_roles = claims["urn:zitadel:iam:org:project:roles"] || []
extract_roles_from_project_roles(project_roles)
end
@doc """
Extracts roles from Zitadel's nested project roles structure.
The structure is typically:
[
%{"role1" => %{"id" => "org1", "primaryDomain" => "domain1"}},
%{"role2" => %{"id" => "org2", "primaryDomain" => "domain2"}}
]
"""
def extract_roles_from_project_roles(project_roles) when is_list(project_roles) do
project_roles
|> Enum.flat_map(fn role_map ->
role_map
|> Map.keys()
|> Enum.reject(&(&1 == "id" || &1 == "primaryDomain"))
end)
|> Enum.map(&String.downcase/1)
|> Enum.uniq()
end
def extract_roles_from_project_roles(_), do: []
@doc """
Extracts scopes from Zitadel claims.
Uses the standard 'scope' claim and splits it into a list.
"""
def get_scopes(claims) do
scope_string = claims["scope"] || ""
scope_string
|> String.split(" ")
|> Enum.reject(&(&1 == ""))
end
@doc """
Checks if the given claims contain a specific role.
"""
def has_role?(claims, role) when is_binary(role) do
roles = get_roles(claims)
role_lower = String.downcase(role)
role_lower in roles
end
@doc """
Checks if the given claims contain a specific scope.
"""
def has_scope?(claims, scope) when is_binary(scope) do
scopes = get_scopes(claims)
scope in scopes
end
@doc """
Validates that all required claims are present.
"""
def validate_claims!(claims) do
_user_id = get_user_id(claims)
_org_id = get_org_id(claims)
:ok
end
end

View File

@@ -8,6 +8,18 @@ defmodule PolicyServiceWeb.Router do
plug OpenApiSpex.Plug.PutApiSpec, module: PolicyServiceWeb.ApiSpec
end
pipeline :authenticated do
plug PolicyServiceWeb.Plugs.AuthenticationPlug,
provider: PolicyService.ZitadelProvider,
client_id: {__MODULE__, :get_zitadel_config, [:client_id]},
client_secret: {__MODULE__, :get_zitadel_config, [:client_secret]},
required_scopes: {__MODULE__, :get_zitadel_config, [:required_scopes]}
end
pipeline :authorized do
plug PolicyServiceWeb.Plugs.AuthorizationPlug
end
get "/health", HealthController, :health
get "/health/ready", HealthController, :ready
@@ -17,6 +29,8 @@ defmodule PolicyServiceWeb.Router do
get "/openapi", OpenApiSpex.Plug.RenderSpec, []
scope "/v1" do
pipe_through [:authenticated, :authorized]
get "/policies", PolicyController, :index
get "/policies/:application_id", PolicyController, :show
post "/policies", PolicyController, :create
@@ -27,4 +41,8 @@ defmodule PolicyServiceWeb.Router do
scope "/swaggerui" do
get "/", OpenApiSpex.Plug.SwaggerUI, path: "/api/openapi"
end
def get_zitadel_config(key) do
Application.get_env(:policy_service, :zitadel)[key]
end
end

View File

@@ -230,8 +230,16 @@ defmodule PolicyServiceWeb.Schemas.Policy do
coverage_amount: %Schema{type: :number, example: 100_000},
coverage_years: %Schema{type: :integer, example: 10},
smoker: %Schema{type: :boolean, example: false},
medications: %Schema{type: :array, items: %Schema{type: :string}, example: ["Aspirin", "Lisinopril"]},
surgeries: %Schema{type: :array, items: %Schema{type: :string}, example: ["Appendectomy, 2015"]},
medications: %Schema{
type: :array,
items: %Schema{type: :string},
example: ["Aspirin", "Lisinopril"]
},
surgeries: %Schema{
type: :array,
items: %Schema{type: :string},
example: ["Appendectomy, 2015"]
},
weight: %Schema{type: :number, example: 70},
height: %Schema{type: :number, example: 175}
}

View File

@@ -62,7 +62,9 @@ defmodule PolicyService.MixProject do
{:flop, "~> 0.26"},
{:req, "~> 0.5"},
{:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false},
{:typedstruct, "~> 0.5"}
{:typedstruct, "~> 0.5"},
{:oidcc, "~> 3.7"},
{:oidcc_plug, "~> 0.4"}
]
end

View File

@@ -23,10 +23,13 @@
"gen_stage": {:hex, :gen_stage, "1.3.2", "7c77e5d1e97de2c6c2f78f306f463bca64bf2f4c3cdd606affc0100b89743b7b", [:mix], [], "hexpm", "0ffae547fa777b3ed889a6b9e1e64566217413d018cabd825f786e843ffe63e7"},
"hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"jose": {:hex, :jose, "1.11.12", "06e62b467b61d3726cbc19e9b5489f7549c37993de846dfb3ee8259f9ed208b3", [:mix, :rebar3], [], "hexpm", "31e92b653e9210b696765cdd885437457de1add2a9011d92f8cf63e4641bab7b"},
"mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"},
"mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"},
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
"oidcc": {:hex, :oidcc, "3.7.2", "2047949832ca7984d6d9c218cc5f23e8096bf50ebb809124d3a01673ee2bfe12", [:mix, :rebar3], [{:igniter, "~> 0.6.3 or ~> 0.7.0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jose, "~> 1.11", [hex: :jose, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_registry, "~> 0.3.1", [hex: :telemetry_registry, repo: "hexpm", optional: false]}], "hexpm", "e3f1ed91509fdeb31ec8b9de4ecda0e80cb68b463a9f5b7a9ee1ee40e521e445"},
"oidcc_plug": {:hex, :oidcc_plug, "0.4.0", "e31ed82f44c0a1685874f7a8574d3ce714603d398c449b8b0c55e89908623979", [:mix], [{:igniter, "~> 0.5.50 or ~> 0.6.0 or ~> 0.7.0", [hex: :igniter, repo: "hexpm", optional: true]}, {:oidcc, "~> 3.7", [hex: :oidcc, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "4d3d6da5f4b51bd9ffc03e4539c631503d459153e6ba31964316c87f4a310068"},
"open_api_spex": {:hex, :open_api_spex, "3.22.2", "0b3c4f572ee69cb6c936abf426b9d84d8eebd34960871fd77aead746f0d69cb0", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0 or ~> 6.0", [hex: :poison, repo: "hexpm", optional: true]}, {:ymlr, "~> 2.0 or ~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :ymlr, repo: "hexpm", optional: true]}], "hexpm", "0a4fc08472d75e9cfe96e0748c6b1565b3b4398f97bf43fcce41b41b6fd3fb33"},
"phoenix": {:hex, :phoenix, "1.8.3", "49ac5e485083cb1495a905e47eb554277bdd9c65ccb4fc5100306b350151aa95", [:mix], [{:bandit, "~> 1.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "36169f95cc2e155b78be93d9590acc3f462f1e5438db06e6248613f27c80caec"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.7.0", "75c4b9dfb3efdc42aec2bd5f8bccd978aca0651dbcbc7a3f362ea5d9d43153c6", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "1d75011e4254cb4ddf823e81823a9629559a1be93b4321a6a5f11a5306fbf4cc"},

View File

@@ -72,6 +72,20 @@ controllers:
secretKeyRef:
name: '{{ include "bjw-s.common.lib.chart.names.fullname" $ }}-cluster-pg-app'
key: uri
# Zitadel Configuration
ZITADEL_ISSUER:
value: "https://zitadel.example.com"
ZITADEL_CLIENT_ID:
valueFrom:
secretKeyRef:
name: '{{ include "bjw-s.common.lib.chart.names.fullname" $ }}-apiapp-client-secret'
key: clientId
ZITADEL_CLIENT_SECRET:
valueFrom:
secretKeyRef:
name: '{{ include "bjw-s.common.lib.chart.names.fullname" $ }}-apiapp-client-secret'
key: clientSecret
probes:
liveness:
enabled: true
@@ -250,3 +264,16 @@ rawResources:
schemas:
- name: eventstore
owner: policy_service
apiapp:
enabled: true
apiVersion: zitadel.github.com/v1alpha1
kind: APIApp
suffix: apiapp
spec:
spec:
projectRef:
name: seguros-dev
namespace: zitadel-resources-operator
apiAppName: policy-service
authMethodType: API_AUTH_METHOD_TYPE_BASIC