portless
Set up and use portless for named local dev server URLs (e.g. https://myapp.localhost instead of http://localhost:3000). Use when…
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
Claude Code
$ npx -y skills add vercel-labs/portless --skill oauth --agent claude-code
Codex
$ npx skills add vercel-labs/portless --skill oauth -a codex -y
Cursor
$ npx skills add vercel-labs/portless --skill oauth -a cursor -y
Gemini CLI
$ npx skills add vercel-labs/portless --skill oauth -a gemini-cli -y
GitHub Copilot
$ npx skills add vercel-labs/portless --skill oauth -a github-copilot -y
OpenCode
$ npx skills add vercel-labs/portless --skill oauth -a opencode -y
Windsurf
$ npx skills add vercel-labs/portless --skill oauth -a windsurf -y
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 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.
When portless uses the default `.localhost` TLD, OAuth providers reject redirect URIs like `http://myapp.localhost:1355/callback`:
| Provider | `localhost` | `.localhost` subdomains | Reason | | --------- | ----------- | ----------------------- | ------------------------------ | | Google | Allowed | Rejected | Not in their bundled PSL | | Apple | Rejected | Rejected | No localhost at all | | Microsoft | Allowed | Allowed | Permissive localhost handling | | Facebook | Allowed | Varies | Must register each URI exactly | | GitHub | Allowed | Allowed | Permissive |
Google and Apple are the strictest. Microsoft and GitHub are more lenient with localhost.
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.
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.
1. Go to [Google Cloud Console > Credentials](https://console.cloud.google.com/apis/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 Sign In does not allow `localhost` or IP addresses at all.
1. Go to [Apple Developer > Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources) 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.
1. Go to [Azure Portal > App registrations](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps) 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.
1. Go to [Meta for Developers > App Dashboard](https://developers.facebook.com/apps/) 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.
1. Go to [GitHub Developer Settings > OAuth Apps](https://github.com/settings/developers) 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.
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.
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.
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`;Replace port numbers with stable, named .localhost URLs for local development. For humans and agents. portless runs a local reverse proxy and gives each dev server a name.
Set up and use portless for named local dev server URLs (e.g. https://myapp.localhost instead of http://localhost:3000). Use when…
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
Ask which skill or flow fits your situation. A router over the skills in this repo.