Skip to content

Commit

Permalink
feat: set oauth origin dynamically based on host header (#427)
Browse files Browse the repository at this point in the history
  • Loading branch information
dbarrosop committed Nov 8, 2023
1 parent c3fcb7f commit 024258c
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 11 deletions.
5 changes: 5 additions & 0 deletions .changeset/lemon-seas-search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'hasura-auth': minor
---

feat: set oauth origin dynamically based on host header
19 changes: 9 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,20 @@ get-version: ## Return version.


.PHONY: dev
dev: check-port install dev-env-up ## Start development environment.
dev: check-port dev-env-up ## Start development environment.
bash -c "trap 'make dev-env-down' EXIT; pnpm dev:start"


.PHONY: test
test: check-port install dev-env-up ## Run end-to-end tests.
test: check-port dev-env-up ## Run end-to-end tests.
pnpm test

.PHONY: check-port
check-port:
[ -z $$(lsof -t -i tcp:$(PORT)) ] || (echo "The port $(PORT) is already in use"; exit 1;)

.PHONY: docgen
docgen: check-port install dev-env-up ## Generate the openapi.json file.
docgen: check-port dev-env-up ## Generate the openapi.json file.
AUTH_CLIENT_URL=https://my-app.com AUTH_LOG_LEVEL=error AUTH_ACCESS_CONTROL_ALLOWED_REDIRECT_URLS= pnpm dev &
while [ "$$(curl -s -o /dev/null -w ''%{http_code}'' http://localhost:$(PORT)/healthz)" != "200" ]; do sleep 1; done
curl http://localhost:$(PORT)/openapi.json | json_pp > docs/openapi.json
Expand All @@ -45,28 +45,27 @@ docgen: check-port install dev-env-up ## Generate the openapi.json file.


.PHONY: watch
watch: check-port install dev-env-up ## Start tests in watch mode.
watch: check-port dev-env-up ## Start tests in watch mode.
bash -c "trap 'make dev-env-down' EXIT; pnpm test:watch"


.PHONY: build
build:
build:
docker build -t $(IMAGE) .


.PHONY: dev-env-down
.PHONY: dev-env-down
dev-env-up: ## Start required services (Hasura, Postgres, Mailhog).
docker-compose -f docker-compose.yaml up -d
docker compose -f docker-compose.yaml up -d
while [ "$$(curl -s -o /dev/null -w ''%{http_code}'' http://localhost:8080/healthz)" != "200" ]; do sleep 1; done
@echo "Hasura is ready";


.PHONY: dev-env-down
dev-env-down: ## Stop required services (Hasura, Posgres, Mailhbg).
docker-compose -f docker-compose.yaml down
docker compose -f docker-compose.yaml down


.PHONY: install
install:
install:
pnpm install

77 changes: 77 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

98 changes: 98 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
{
description = "Nhost Hasura Auth";

inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
nix-filter.url = "github:numtide/nix-filter";
flake-utils.url = "github:numtide/flake-utils";
};

outputs = { self, nixpkgs, flake-utils, nix-filter }:
flake-utils.lib.eachDefaultSystem (system:
let
overlays = [
(final: prev: {
nodejs = prev.nodejs-18_x;
})
];

pkgs = import nixpkgs {
inherit overlays system;
};

nix-src = nix-filter.lib.filter {
root = ./.;
include = [
(nix-filter.lib.matchExt "nix")
];
};

node_modules = pkgs.stdenv.mkDerivation {
inherit version;

pname = "node_modules";

nativeBuildInputs = with pkgs; [
nodePackages.pnpm
];

src = nix-filter.lib.filter {
root = ./.;
include = [
./package.json
./pnpm-lock.yaml
];
};

buildPhase = ''
pnpm install
'';

installPhase = ''
mkdir -p $out
cp -r node_modules $out
'';
};


name = "hasura-auth";
version = "0.0.0-dev";

buildInputs = [ ];
nativeBuildInputs = with pkgs; [
nodePackages.pnpm
];
in
{
checks = {
nixpkgs-fmt = pkgs.runCommand "check-nixpkgs-fmt"
{
nativeBuildInputs = with pkgs;
[
nixpkgs-fmt
];
}
''
mkdir $out
nixpkgs-fmt --check ${nix-src}
'';

};

devShells = flake-utils.lib.flattenTree rec {
default = pkgs.mkShell {
buildInputs = with pkgs; [
nixpkgs-fmt
gnumake
] ++ buildInputs ++ nativeBuildInputs;

shellHook = ''
export PATH=${node_modules}/node_modules/.bin:$PATH
rm -rf node_modules
ln -sf ${node_modules}/node_modules/ node_modules
'';
};
};
}
);
}
14 changes: 14 additions & 0 deletions src/routes/oauth/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export const PROVIDERS_CONFIG: Record<
response_type: 'code id_token',
response_mode: 'form_post',
},
dynamic: [],
},
profile: ({ jwt, profile }) => {
const payload = jwt?.id_token?.payload;
Expand Down Expand Up @@ -93,6 +94,7 @@ export const PROVIDERS_CONFIG: Record<
access_url: `${azureBaseUrl}/[subdomain]/oauth2/token`,
profile_url: `${azureBaseUrl}/[subdomain]/openid/userinfo`,
subdomain: process.env.AUTH_PROVIDER_AZUREAD_TENANT || 'common',
dynamic: [],
},
profile: ({ jwt }) => {
const payload = jwt?.id_token?.payload;
Expand All @@ -109,6 +111,7 @@ export const PROVIDERS_CONFIG: Record<
client_id: process.env.AUTH_PROVIDER_BITBUCKET_CLIENT_ID,
client_secret: process.env.AUTH_PROVIDER_BITBUCKET_CLIENT_SECRET,
scope: ['account'],
dynamic: [],
},
profile: async ({ profile, access_token }) => {
const {
Expand All @@ -135,6 +138,7 @@ export const PROVIDERS_CONFIG: Record<
client_id: process.env.AUTH_PROVIDER_DISCORD_CLIENT_ID,
client_secret: process.env.AUTH_PROVIDER_DISCORD_CLIENT_SECRET,
scope: ['identify', 'email'],
dynamic: [],
},
profile: ({ profile }) => ({
id: profile.id,
Expand All @@ -152,6 +156,7 @@ export const PROVIDERS_CONFIG: Record<
client_secret: process.env.AUTH_PROVIDER_FACEBOOK_CLIENT_SECRET,
scope: ['email'],
profile_url: 'https://graph.facebook.com/me?fields=id,name,email,picture',
dynamic: [],
},
profile: ({ profile }) => ({
id: profile.id,
Expand All @@ -166,6 +171,7 @@ export const PROVIDERS_CONFIG: Record<
client_id: process.env.AUTH_PROVIDER_GITHUB_CLIENT_ID,
client_secret: process.env.AUTH_PROVIDER_GITHUB_CLIENT_SECRET,
scope: ['user:email'],
dynamic: [],
},
profile: async ({ profile, access_token }) => {
// * The email is not returned by default, so we need to make a separate request
Expand All @@ -191,6 +197,7 @@ export const PROVIDERS_CONFIG: Record<
client_id: process.env.AUTH_PROVIDER_GITLAB_CLIENT_ID,
client_secret: process.env.AUTH_PROVIDER_GITLAB_CLIENT_SECRET,
scope: ['read_user'],
dynamic: [],
},
profile: ({ profile }) => ({
id: profile.id && String(profile.id),
Expand All @@ -209,6 +216,7 @@ export const PROVIDERS_CONFIG: Record<
prompt: 'consent',
access_type: 'offline',
},
dynamic: [],
},
profile: ({
profile: { sub, name, picture, email, email_verified, locale },
Expand All @@ -229,6 +237,7 @@ export const PROVIDERS_CONFIG: Record<
scope: ['r_emailaddress', 'r_liteprofile'],
profile_url:
'https://api.linkedin.com/v2/me?projection=(id,localizedFirstName,localizedLastName,profilePicture(displayImage~:playableStreams))',
dynamic: [],
},
profile: async ({ profile, access_token }) => {
const {
Expand Down Expand Up @@ -277,6 +286,7 @@ export const PROVIDERS_CONFIG: Record<
client_id: process.env.AUTH_PROVIDER_SPOTIFY_CLIENT_ID,
client_secret: process.env.AUTH_PROVIDER_SPOTIFY_CLIENT_SECRET,
scope: ['user-read-email', 'user-read-private'],
dynamic: [],
},
profile: ({ profile }) => ({
id: profile.id,
Expand All @@ -291,6 +301,7 @@ export const PROVIDERS_CONFIG: Record<
client_id: process.env.AUTH_PROVIDER_STRAVA_CLIENT_ID,
client_secret: process.env.AUTH_PROVIDER_STRAVA_CLIENT_SECRET,
scope: ['profile:read_all'],
dynamic: [],
},
// ! It is not possible to get the user's email address from Strava
profile: ({ profile }) => {
Expand All @@ -307,6 +318,7 @@ export const PROVIDERS_CONFIG: Record<
client_id: process.env.AUTH_PROVIDER_TWITCH_CLIENT_ID,
client_secret: process.env.AUTH_PROVIDER_TWITCH_CLIENT_SECRET,
scope: ['user:read:email'],
dynamic: [],
},
profile: ({ profile: { data } }) => {
if (!Array.isArray(data)) {
Expand Down Expand Up @@ -336,6 +348,7 @@ export const PROVIDERS_CONFIG: Record<
response: ['tokens', 'profile', 'raw'],
profile_url:
'https://api.twitter.com/1.1/account/verify_credentials.json?include_email=true',
dynamic: [],
},
profile: ({ profile }) => ({
id: profile.id_str || (profile.id && String(profile.id)),
Expand All @@ -355,6 +368,7 @@ export const PROVIDERS_CONFIG: Record<
client_id: process.env.AUTH_PROVIDER_WINDOWS_LIVE_CLIENT_ID,
client_secret: process.env.AUTH_PROVIDER_WINDOWS_LIVE_CLIENT_SECRET,
scope: ['wl.basic', 'wl.emails'],
dynamic: [],
},
profile: ({ profile }) => ({
// ? Could be improved in fetching the user's profile picture - but the apis.live.net/v5.0 API is deprecated
Expand Down
6 changes: 6 additions & 0 deletions src/routes/oauth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ export const oauthProviders = Router()
* Grant middleware: handle the oauth flow until the callback
* @see {@link file://./config/index.ts}
*/
.use((req, res, next) => {
res.locals.grant = {dynamic: {
origin: `${req.protocol}://${req.headers.host}`},
};
next();
})
.use(grant.express(grantConfig))

/**
Expand Down
1 change: 0 additions & 1 deletion src/routes/oauth/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ export const createGrantConfig = (): GrantConfig =>
},
{
defaults: {
origin: ENV.AUTH_SERVER_URL,
prefix: `${ENV.AUTH_API_PREFIX}${OAUTH_ROUTE}`,
transport: 'session',
scope: ['email', 'profile'],
Expand Down

0 comments on commit 024258c

Please sign in to comment.