Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: set oauth origin dynamically based on host header #427

Merged
merged 7 commits into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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}`,
onehassan marked this conversation as resolved.
Show resolved Hide resolved
transport: 'session',
scope: ['email', 'profile'],
Expand Down