Skip to content

Commit

Permalink
Merge pull request #1 from tobiasbernet/feature/openid_connector
Browse files Browse the repository at this point in the history
Add OpenID-Connector
  • Loading branch information
tobiasbernet authored Aug 16, 2021
2 parents cb14a68 + 28dd8dc commit 4ea81b8
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 22 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@ To start your Phoenix server:
* Install Node.js dependencies with `cd assets && npm install`
* Start Phoenix endpoint with `mix phx.server`

Now you can visit [`localhost:4040`](http://localhost:4040) from your browser.
Start schlusseli and keycloak: `./startup.sh`.

Ready to run in production? Please [check our deployment guides](https://hexdocs.pm/phoenix/deployment.html).
Keycloak: Now you can visit [`http://127.0.0.1:8085/auth/`](http://127.0.0.1:8085/auth/) from your browser.

Start schlusseli and keycloa: `./startup.sh`
## Keycloak setup

Create 3 clients:

* `schlusseli-api`: Bearer-only client. The API backend service.
* `schlusseli-ui`: Confidential client has access to the `schlusseli-api` service.
* `evil-ui`: Confidential client, has no access to the `schlusseli-api`.

## Learn more

Expand All @@ -20,4 +26,4 @@ Start schlusseli and keycloa: `./startup.sh`
* Docs: https://hexdocs.pm/phoenix
* Mailing list: http://groups.google.com/group/phoenix-talk
* Source: https://github.com/phoenixframework/phoenix
# schlusseli
#
20 changes: 11 additions & 9 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,18 @@ config :logger, :console,
# Use Jason for JSON parsing in Phoenix
config :phoenix, :json_library, Jason

# OpenID provider config
config :schlusseli, :openid_connect_providers,
keycloak: [
discovery_document_uri: "http://127.0.0.1:8085/auth/realms/Schlusseli/.well-known/openid-configuration",
client_id: "schlusseli-api",
client_secret: "9daec6ad-c6ac-4cde-b955-dc846c2cfbbb",
redirect_uri: "",
response_type: "code",
scope: "api-service",
verify_token_audience: true
]

config :keycloak,
realm: "heidi",
site: "http://127.0.0.1:8080",
client_id: "heidi-api",
client_secret: ""

config :keycloak, Schlusseli.Plug.VerifyToken,
hmac: "0617b135-6bba-4af0-96a7-5e34b7a1aead"
#public_key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhGXASBTDByDsd2BfN1oBLNHveUlHIDonEhhK//lnvKn1GTFhbuId0szZQG4Eenk5laZqcBvbW4WjNuNKV6FtZjQLPUIaJbYMO2g/R4eKsnaQVK8ynHewcbikgB/JhSmw9WknyISmtC+pgSksnilHM2os0V6O5shxCk3ZFRvA6X14PdeQ5ugNilfe2oIhAkO0W+iJgqTIp7EGqYy/wd4M/7QbaoB+wKU1fWjWz+046zZnlAct4KgDi5MlITkuIqbjVOqC4cI+rZk4W459ETJeENowgegx0PXPvTTV1wX63Z8kKElcQewLfp6nA9nR1a9B0SX/6xvwNMXKzVWzjiLZswIDAQAB"


# Import environment specific config. This must remain at the bottom
Expand Down
5 changes: 3 additions & 2 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ services:
postgres:
image: postgres
ports:
- 5440:5640
- 5640:5640
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
Expand All @@ -17,6 +17,7 @@ services:
POSTGRES_PASSWORD: postgres
keycloak:
image: quay.io/keycloak/keycloak:latest
command: ["-Djboss.http.port=8085"]
environment:
DB_VENDOR: POSTGRES
DB_ADDR: postgres
Expand All @@ -27,6 +28,6 @@ services:
KEYCLOAK_USER: admin
KEYCLOAK_PASSWORD: admin
ports:
- 8080:8081
- 8085:8085
depends_on:
- postgres
6 changes: 3 additions & 3 deletions lib/schlusseli/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ defmodule Schlusseli.Application do
# List all child processes to be supervised
children = [
# Start the endpoint when the application starts
SchlusseliWeb.Endpoint
# Starts a worker by calling: Schlusseli.Worker.start_link(arg)
# {Schlusseli.Worker, arg},
SchlusseliWeb.Endpoint,
# Starts the openID worker
{OpenIDConnect.Worker, Application.get_env(:schlusseli, :openid_connect_providers)}
]

# See https://hexdocs.pm/elixir/Supervisor.html
Expand Down
70 changes: 70 additions & 0 deletions lib/schlusseli_web/plug/openid_connector.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
defmodule Schlusseli.Plug.OpenidConnector do
@moduledoc """
Plug for verifying authorization on a per request basis, verifies that a token is set in the
`Authorization` header.
Problem: OpenIDConnect verifies the token with the public key. No `introspec` is made. We don't know if the token is valid or not.
"""

import Plug.Conn

@doc false
def init(opts), do: opts

@doc """
Fetches the `Authorization` header, and verifies the token if present. If a
valid token is passed, the decoded `%Joken.Token{}` is added as `:token`
to the `conn` assigns.
"""
@spec call(Plug.Conn.t(), keyword()) :: Plug.Conn.t()
def call(conn, _) do
conn
|> get_req_header("authorization")
|> fetch_token()
|> verify_token(conn)
end

def fetch_token([token]) when is_binary(token) do
token
|> String.split(" ")
|> List.last()
end

def fetch_token(_), do: nil

@doc """
Verifies the token with OpenIDConnect and adds the claims to the conn.
"""
def verify_token(token, conn, auth_provider \\ :keycloak) do
with {:ok, claims} <- OpenIDConnect.verify(auth_provider, token),
true <- verify_audience(claims, get_provider_conf(:verify_token_audience)) do
conn
|> assign(:claims, claims)
else
_ -> auth_error(conn)
end
end

@doc """
Checks the audience if the `verify token audience` is `true`.
"""
def verify_audience(%{"aud" => aud}, true) when is_binary(aud) do
aud == get_provider_conf(:client_id)
end

def verify_audience(_, true), do: false

def verify_audience(_, _), do: true

defp get_provider_conf(key, auth_provider \\ :keycloak) do
Application.get_env(:schlusseli, :openid_connect_providers)
|> get_in([auth_provider, key])
end

defp auth_error(conn) do
conn
|> put_resp_content_type("application/vnd.api+json")
|> send_resp(401, Poison.encode!(%{error: :not_authorized}))
|> halt()
end
end
1 change: 1 addition & 0 deletions lib/schlusseli_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ defmodule SchlusseliWeb.Router do

pipeline :api do
plug :accepts, ["json"]
plug(Schlusseli.Plug.OpenidConnector)
end

scope "/", SchlusseliWeb do
Expand Down
3 changes: 2 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ defmodule Schlusseli.MixProject do
{:absinthe_plug, "~> 1.4"},
{:keycloak, "~> 1.0.0"},
{:joken, "~> 2.3.0"},
{:credo, "~> 1.4", only: [:dev, :test], runtime: false}
{:credo, "~> 1.4", only: [:dev, :test], runtime: false},
{:openid_connect, "~> 0.1.1"}
]
end

Expand Down
8 changes: 5 additions & 3 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@
"cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"},
"credo": {:hex, :credo, "1.5.6", "e04cc0fdc236fefbb578e0c04bd01a471081616e741d386909e527ac146016c6", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "4b52a3e558bd64e30de62a648518a5ea2b6e3e5d2b164ef5296244753fc7eb17"},
"db_connection": {:hex, :db_connection, "2.4.0", "d04b1b73795dae60cead94189f1b8a51cc9e1f911c234cc23074017c43c031e5", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ad416c21ad9f61b3103d254a71b63696ecadb6a917b36f563921e0de00d7d7c8"},
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
"decimal": {:hex, :decimal, "1.9.0", "83e8daf59631d632b171faabafb4a9f4242c514b0a06ba3df493951c08f64d07", [:mix], [], "hexpm", "b1f2343568eed6928f3e751cf2dffde95bfaa19dd95d09e8a9ea92ccfd6f7d85"},
"ecto": {:hex, :ecto, "3.6.2", "efdf52acfc4ce29249bab5417415bd50abd62db7b0603b8bab0d7b996548c2bc", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "efad6dfb04e6f986b8a3047822b0f826d9affe8e4ebdd2aeedbfcb14fd48884e"},
"ecto_sql": {:hex, :ecto_sql, "3.6.2", "9526b5f691701a5181427634c30655ac33d11e17e4069eff3ae1176c764e0ba3", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.6.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.4.0 or ~> 0.5.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5ec9d7e6f742ea39b63aceaea9ac1d1773d574ea40df5a53ef8afbd9242fdb6b"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
"gettext": {:hex, :gettext, "0.18.2", "7df3ea191bb56c0309c00a783334b288d08a879f53a7014341284635850a6e55", [:mix], [], "hexpm", "f9f537b13d4fdd30f3039d33cb80144c3aa1f8d9698e47d7bcbcc8df93b1f5c5"},
"hackney": {:hex, :hackney, "1.17.4", "99da4674592504d3fb0cfef0db84c3ba02b4508bae2dff8c0108baa0d6e0977c", [:rebar3], [{:certifi, "~>2.6.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "de16ff4996556c8548d512f4dbe22dd58a587bf3332e7fd362430a7ef3986b16"},
"httpoison": {:hex, :httpoison, "1.2.0", "2702ed3da5fd7a8130fc34b11965c8cfa21ade2f232c00b42d96d4967c39a3a3", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "712d9b1dfde2470ed000dff4b2faddaf56d038e5ac1944abc0a81c901a368391"},
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
"jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"},
"jason": {:hex, :jason, "1.0.1", "ef108e64c6e086364b9f15b0073cf794061670af8f331d545d7308c0ba2e67f9", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "51f68891a38d5186ba6a4c0c148f59b33d20b068168976fd25bdfc0f57347f0b"},
"joken": {:hex, :joken, "2.3.0", "62a979c46f2c81dcb8ddc9150453b60d3757d1ac393c72bb20fc50a7b0827dc6", [:mix], [{:jose, "~> 1.10", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "57b263a79c0ec5d536ac02d569c01e6b4de91bd1cb825625fe90eab4feb7bc1e"},
"jose": {:hex, :jose, "1.11.2", "f4c018ccf4fdce22c71e44d471f15f723cb3efab5d909ab2ba202b5bf35557b3", [:mix, :rebar3], [], "hexpm", "98143fbc48d55f3a18daba82d34fe48959d44538e9697c08f34200fa5f0947d2"},
"keycloak": {:hex, :keycloak, "1.0.0", "6ef7d5ffbb511edc539418ff2b2e2c8f3410c4f39f812b001ca3211d90c33c0c", [:mix], [{:joken, "~> 2.0", [hex: :joken, repo: "hexpm", optional: false]}, {:oauth2, "~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 4.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "49b3cc53ffe440b1e84ac9090df79e7d465cef2ed31bc677258ec8f02558ea95"},
Expand All @@ -25,8 +26,9 @@
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
"nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"},
"oauth2": {:hex, :oauth2, "2.0.0", "338382079fe16c514420fa218b0903f8ad2d4bfc0ad0c9f988867dfa246731b0", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "881b8364ac7385f9fddc7949379cbe3f7081da37233a1aa7aab844670a91e7e7"},
"openid_connect": {:hex, :openid_connect, "0.1.2", "166bb5c413c0967fe2c5fb1f3f3bfacd44803008593b587f55f7cb55b6e23124", [:mix], [{:httpoison, "~> 1.2.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jason, "~> 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "c4b275526acf9ebbfef89db44619869869560a275dd5bb30524718e4696ab15a"},
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
"phoenix": {:hex, :phoenix, "1.4.17", "1b1bd4cff7cfc87c94deaa7d60dd8c22e04368ab95499483c50640ef3bd838d8", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3a8e5d7a3d76d452bb5fb86e8b7bd115f737e4f8efe202a463d4aeb4a5809611"},
"phoenix": {:hex, :phoenix, "1.4.0", "56fe9a809e0e735f3e3b9b31c1b749d4b436e466d8da627b8d82f90eaae714d2", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "22da8f659cf13d3ba73b767f66b8c389113ddf0ef7b94225cc84e94b85eac90e"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.3.0", "2c69a452c2e0ee8c93345ae1cdc1696ef4877ff9cbb15c305def41960c3c4ebf", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "0ac491924217550c8f42c81c1f390b5d81517d12ceaf9abf3e701156760a848e"},
"phoenix_html": {:hex, :phoenix_html, "2.14.3", "51f720d0d543e4e157ff06b65de38e13303d5778a7919bcc696599e5934271b8", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "efd697a7fff35a13eeeb6b43db884705cba353a1a41d127d118fda5f90c8e80f"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.3", "3a53772a6118d5679bf50fc1670505a290e32a1d195df9e069d8c53ab040c054", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "766796676e5f558dbae5d1bdb066849673e956005e3730dfd5affd7a6da4abac"},
Expand Down

0 comments on commit 4ea81b8

Please sign in to comment.