Fireauth
Firebase Auth helpers for Elixir apps:
- Verify Firebase ID tokens (RS256) using Google's SecureToken x509 certs.
- (Optional) Mint Firebase session cookies (server-side, requires admin credentials via a service account).
- Plug helpers for both approaches.
Install
Add to your mix.exs
{:fireauth, "~> 0.2.0"},Two Ways To Use This Library
1) ID Tokens (Bearer Headers, No Admin Credentials)
This is the simplest integration if your client is JavaScript and can send:
Authorization: Bearer <idToken>
Fireauth will verify that ID token and attach claims/user info to conn.assigns.
This does not require any Firebase Admin service account.
Minimal Plug Example
defmodule MyApp.Router do
use Plug.Router
import Plug.Conn
plug :match
# Verifies `Authorization: Bearer <idToken>` and assigns `conn.assigns.fireauth`.
plug Fireauth.Plug,
serve_hosted_auth?: false,
# Optional if you set `config :fireauth, firebase_project_id: "..."` (or `FIREBASE_PROJECT_ID`).
project_id: "your-project-id",
on_invalid_token: :unauthorized
plug :dispatch
get "/protected" do
case conn.assigns[:fireauth] do
%{claims: claims, user_attrs: user} ->
json(conn, 200, %{
"uid" => user.firebase_uid,
"email" => user.email,
"iss" => claims.iss,
"aud" => claims.aud
})
_ ->
send_resp(conn, 401, "unauthorized")
end
end
match _ do
send_resp(conn, 404, "not_found")
end
defp json(conn, status, data) do
conn
|> put_resp_content_type("application/json")
|> send_resp(status, Jason.encode!(data))
end
end2) Session Cookies (Phoenix/LiveView, Requires Admin Credentials To Mint)
If you want a traditional Phoenix/LiveView setup using an httpOnly cookie:
-
Client signs in with Firebase JS SDK and obtains an
idToken -
Client POSTs that
idTokento your backend -
Backend exchanges it for a Firebase session cookie and sets it as
httpOnly
That exchange requires admin credentials (typically a Firebase Admin service account). You do not need the Admin SDK library, but you do need the service account credentials.
Minimal Plug Example
defmodule MyApp.Router do
use Plug.Router
import Plug.Conn
plug :match
# 1) Mount endpoints for:
# GET /auth/csrf
# POST /auth/session (exchange idToken -> httpOnly session cookie)
# POST /auth/logout
forward "/auth",
to: Fireauth.Plug.SessionRouter,
init_opts: [
# Optional if you set `config :fireauth, firebase_project_id: "..."` (or `FIREBASE_PROJECT_ID`).
project_id: "your-project-id",
# For local dev only; in prod you want true.
cookie_secure: false,
# Optional. Default is 5 days. Must be between 300 and 1_209_600 seconds.
valid_duration_s: 60 * 60 * 24 * 14
]
# 2) Verify the httpOnly cookie on every request and assign `conn.assigns.fireauth`.
plug Fireauth.Plug.SessionCookie,
# Optional if you set `config :fireauth, firebase_project_id: "..."` (or `FIREBASE_PROJECT_ID`).
project_id: "your-project-id",
on_invalid_cookie: :unauthorized
plug :dispatch
get "/protected" do
case conn.assigns[:fireauth] do
%{claims: claims, user_attrs: user} ->
json(conn, 200, %{
"uid" => user.firebase_uid,
"email" => user.email,
"iss" => claims.iss,
"aud" => claims.aud
})
_ ->
send_resp(conn, 401, "unauthorized")
end
end
match _ do
send_resp(conn, 404, "not_found")
end
defp json(conn, status, data) do
conn
|> put_resp_content_type("application/json")
|> send_resp(status, Jason.encode!(data))
end
endConfiguration
Set your Firebase project id:
config :fireauth, firebase_project_id: "your-project-id"
Or via env var: FIREBASE_PROJECT_ID.
Session Cookies (Admin Service Account)
To mint session cookies, configure a Firebase Admin service account (JSON) either in config or via env var.
Config:
config :fireauth, firebase_admin_service_account: %{
"client_email" => "firebase-adminsdk-...@your-project.iam.gserviceaccount.com",
"private_key" => "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",
"project_id" => "your-project-id"
}Env var (JSON or base64-encoded JSON):
FIREBASE_ADMIN_SERVICE_ACCOUNT='{"type":"service_account",...}'FIREBASE_ADMIN_SERVICE_ACCOUNT='<base64-json>'
Usage
Token Verification & Identity Helpers
{:ok, claims} = Fireauth.verify_id_token(id_token)
user = Fireauth.User.from_claims(claims)
# Check for specific identities (works with both claims and user structs)
if Fireauth.has_identity?(user, "google.com") do
google_uid = Fireauth.identity(user, "google.com")
endCreate Session Cookie
{:ok, session_cookie} = Fireauth.create_session_cookie(id_token, valid_duration_s: 60 * 60 * 24 * 5)This makes a network call to Google (Identity Toolkit) to exchange the ID token for a session cookie, and requires service account credentials.
Hosted Auth Files (Optional)
Fireauth.Plug can also proxy or serve Firebase hosted auth helper files at
/__/auth/* and /__/firebase/init.json. Enable this if you want redirect-mode
auth to work on your own domain in modern browsers.
plug Fireauth.Plug,
# Enable hosted auth file handling.
serve_hosted_auth?: true,
# :proxy (default) fetches from firebaseapp.com. :static serves local copies.
hosted_auth_mode: :proxy,
# Optional if you set `config :fireauth, firebase_project_id: "..."` (or `FIREBASE_PROJECT_ID`).
project_id: "your-project-id"Hosted Auth Modes
To support redirect-mode auth in modern browsers (avoiding third-party cookie issues), you must serve Firebase's helper files from your own domain.
:proxy(Default): Transparently proxies requests tohttps://<project>.firebaseapp.com. This is the most robust method. Responses are cached in-memory.:static: Serves local copies of the helper files embedded in thefireauthlibrary. Use this if your environment cannot make outbound requests to Firebase at runtime.
Caching
This library caches /__/auth/* calls in addition to the Google public keys used
to verify ID tokens and session cookies.
License
MIT