PluginBench
Skill
Official
Pass
Audit score 90

oauth

vercel-labs/portless

Configure OAuth providers to work with portless local dev URLs, fixing redirect URI mismatches.

What is oauth?

Skill for setting up OAuth redirect URIs with portless-served domains instead of localhost. Use when OAuth providers reject .localhost subdomains, you encounter redirect_uri_mismatch errors, or need to configure Google, Apple, Microsoft, Facebook, or GitHub sign-in for local development.

  • Map OAuth redirect URIs to valid TLDs (.dev, .app, .com, etc.) that providers accept
  • Configure provider-specific OAuth settings (Google Cloud, Apple Developer, Azure, Meta, GitHub)
  • Set environment variables (NEXTAUTH_URL, AUTH_URL, BASE_URL) to match portless domains
  • Troubleshoot redirect URI mismatches and provider validation failures
  • Support team setups with wildcard DNS records for shared redirect URIs

How to install oauth

npx skills add https://github.com/vercel-labs/portless --skill oauth
Prerequisites
  • Portless installed and proxy running with a valid TLD (e.g., portless proxy start --tld dev)
  • Access to OAuth provider consoles (Google Cloud, Apple Developer, Azure, Meta, GitHub)
  • Understanding of OAuth redirect URIs and callback URL structure
Claude Code
Cursor
Windsurf
Cline

How to use oauth

  1. 1.Start portless proxy with a valid TLD: portless proxy start --tld dev (or use a domain you own like --tld local.yourcompany.dev)
  2. 2.Register the portless domain with your OAuth provider (e.g., https://myapp.dev)
  3. 3.Add the callback URI to the provider (e.g., https://myapp.dev/api/auth/callback/google)
  4. 4.Set the environment variable matching your auth library (NEXTAUTH_URL, AUTH_URL, or BASE_URL) to the portless domain
  5. 5.Start your app via portless (e.g., portless myapp next dev) and test the OAuth flow

Use cases

Good for
  • Setting up Google OAuth with portless when .localhost subdomains are rejected
  • Configuring Apple Sign In which blocks localhost entirely
  • Fixing redirect_uri_mismatch errors by registering correct portless domains with providers
  • Setting up multi-developer teams with shared OAuth credentials via wildcard DNS
  • Migrating local auth setup from localhost to a valid TLD for provider compatibility
Who it's for
  • Full-stack developers building OAuth-authenticated apps locally
  • Teams sharing OAuth credentials across multiple developers
  • Developers using Next.js + NextAuth or Auth.js for local development
  • Anyone integrating Google, Apple, Microsoft, Facebook, or GitHub sign-in locally

oauth FAQ

Why does Google reject .localhost subdomains?

.localhost subdomains are not in Google's bundled Public Suffix List, so they fail domain validation. Use a real TLD like .dev, .app, or .com instead.

Can I use any TLD with portless?

Any TLD in the Public Suffix List works. For teams, use a multi-segment TLD under a domain you own (e.g., local.yourcompany.dev) to avoid collisions and ensure all developers share the same redirect URIs.

Does Apple allow localhost for OAuth?

No, Apple Sign In blocks localhost and IP addresses entirely. You must use a real domain name, optionally with a public DNS A record pointing to 127.0.0.1 if Apple's server-side validation requires it.

What if my callback goes to localhost instead of the portless domain?

Your auth library is constructing the callback URL from localhost. Set the appropriate environment variable: NEXTAUTH_URL for NextAuth, AUTH_URL for Auth.js v5, or BASE_URL for manual setups.

Do I need HTTPS with portless OAuth?

.dev and .app TLDs are HSTS-preloaded and require HTTPS. Portless defaults to HTTPS on port 443; run portless trust to add the local CA to your system trust store.

Full instructions (SKILL.md)

Source of truth, from vercel-labs/portless.


name: oauth description: Configure OAuth providers (Google, Apple, Microsoft, Facebook, GitHub, etc.) to work with portless local dev URLs. Use when setting up OAuth redirect URIs, fixing "redirect_uri_mismatch" or "invalid redirect" errors, configuring sign-in providers for local development, or when a provider rejects .localhost subdomains. Triggers include "OAuth not working with portless", "redirect URI mismatch", "Google/Apple/Microsoft sign-in fails locally", "configure OAuth for local dev", or any task involving OAuth callback URLs with portless domains.

OAuth with Portless

OAuth providers validate redirect URIs against domain rules. .localhost subdomains fail on most providers because they are not in the Public Suffix List or are explicitly blocked. Portless fixes this with --tld to serve apps on real, valid domains.

The Problem

When portless uses the default .localhost TLD, OAuth providers reject redirect URIs like http://myapp.localhost:1355/callback:

Providerlocalhost.localhost subdomainsReason
GoogleAllowedRejectedNot in their bundled PSL
AppleRejectedRejectedNo localhost at all
MicrosoftAllowedAllowedPermissive localhost handling
FacebookAllowedVariesMust register each URI exactly
GitHubAllowedAllowedPermissive

Google and Apple are the strictest. Microsoft and GitHub are more lenient with localhost.

The Fix

Use a valid TLD so the redirect URI passes provider validation:

portless proxy start --tld dev
portless myapp next dev
# -> https://myapp.dev

Any TLD in the Public Suffix List works: .dev, .app, .com, .io, etc.

Use a domain you own

Bare TLDs like .dev mean myapp.dev could collide with a real domain. Use a multi-segment TLD under a domain you control, so the app name stays clean and the domain structure lives in the TLD:

portless proxy start --tld local.yourcompany.dev
portless myapp next dev
# -> https://myapp.local.yourcompany.dev

This ensures no outbound traffic reaches something you don't own. For teams, set a wildcard DNS record (*.local.yourcompany.dev -> 127.0.0.1) so every developer gets resolution without /etc/hosts, and every developer shares the same redirect URIs in the provider console.

Provider Setup

Google

  1. Go to Google Cloud Console > Credentials
  2. Create or edit an OAuth 2.0 Client ID (Web application)
  3. Add the portless domain to Authorized JavaScript origins: https://myapp.dev
  4. Add the callback to Authorized redirect URIs: https://myapp.dev/api/auth/callback/google

Google validates domains against the Public Suffix List. The domain must end with a recognized TLD. .localhost subdomains fail this check; .dev, .app, .com, etc. all pass.

HTTPS is required for .dev and .app (HSTS-preloaded). Portless handles this automatically with --https.

Apple

Apple Sign In does not allow localhost or IP addresses at all.

  1. Go to Apple Developer > Certificates, Identifiers & Profiles
  2. Register a Services ID
  3. Configure Sign In with Apple, adding the portless domain as a Return URL: https://myapp.dev/api/auth/callback/apple

The domain must be a real, publicly-resolvable domain name. Since portless maps the domain to 127.0.0.1 locally, the browser resolves it but Apple's server-side validation may require the domain to resolve publicly too. If Apple rejects the domain, add a public DNS A record pointing to 127.0.0.1 for your dev subdomain.

Microsoft (Entra / Azure AD)

  1. Go to Azure Portal > App registrations
  2. Create or edit an app registration
  3. Under Authentication, add a Web redirect URI: https://myapp.dev/api/auth/callback/azure-ad

Microsoft allows http://localhost with any port for development. It also accepts .localhost subdomains in most cases. Using a custom TLD with portless is still recommended for consistency across providers.

Facebook (Meta)

  1. Go to Meta for Developers > App Dashboard
  2. Under Facebook Login > Settings, add the portless URL to Valid OAuth Redirect URIs: https://myapp.dev/api/auth/callback/facebook

Facebook requires each redirect URI to be registered exactly (no wildcards). Strict Mode (enabled by default) enforces exact matching.

GitHub

  1. Go to GitHub Developer Settings > OAuth Apps
  2. Set Authorization callback URL: https://myapp.dev/api/auth/callback/github

GitHub is permissive with localhost and subdomains. A custom TLD is not strictly required but keeps the setup consistent.

Auth Library Configuration

NextAuth / Auth.js

Set NEXTAUTH_URL to match the portless domain:

NEXTAUTH_URL=https://myapp.dev

NextAuth uses this to construct callback URLs. Without it, callbacks may use localhost and cause a mismatch.

Passport.js

Set the callbackURL in each strategy to use the portless domain:

new GoogleStrategy({
  clientID: process.env.GOOGLE_CLIENT_ID,
  clientSecret: process.env.GOOGLE_CLIENT_SECRET,
  callbackURL: process.env.BASE_URL + "/auth/google/callback",
});

Set BASE_URL=https://myapp.dev in your environment.

Generic / Manual

Read the PORTLESS_URL environment variable that portless injects into the child process:

const baseUrl = process.env.PORTLESS_URL || "http://localhost:3000";
const callbackUrl = `${baseUrl}/auth/callback`;

Troubleshooting

"redirect_uri_mismatch" or "invalid redirect URI"

The redirect URI sent during the OAuth flow doesn't match what's registered with the provider. Check:

  1. The provider's registered redirect URI matches the portless domain exactly (protocol, host, path)
  2. NEXTAUTH_URL or equivalent is set to the portless URL (not localhost)
  3. The proxy is running with the correct TLD (portless list to verify)

Provider requires HTTPS

.dev and .app TLDs are HSTS-preloaded, so browsers force HTTPS. Start the proxy:

portless proxy start --tld dev

Portless defaults to HTTPS on port 443 (auto-elevates with sudo). Run portless trust to add the local CA to your system trust store and eliminate browser warnings.

Apple rejects the domain

Apple may require the domain to resolve publicly. Add a DNS A record for your dev subdomain pointing to 127.0.0.1:

myapp.local.yourcompany.dev  A  127.0.0.1

Or use a wildcard: *.local.yourcompany.dev A 127.0.0.1.

Callback goes to wrong URL after sign-in

The auth library is constructing the callback URL from localhost instead of the portless domain. Set the appropriate environment variable:

  • NextAuth: NEXTAUTH_URL=https://myapp.dev
  • Auth.js v5: AUTH_URL=https://myapp.dev
  • Manual: PORTLESS_URL is injected automatically; use it as the base URL

Example

See examples/google-oauth for a complete working example with Next.js + NextAuth + Google OAuth using --tld dev.