From vercel-labs-portless
Configures OAuth providers (Google, Apple, Microsoft, Facebook, GitHub) for portless local dev by using valid TLDs to fix redirect URI mismatch and invalid redirect errors.
npx claudepluginhub joshuarweaver/cascade-code-devops-misc-1 --plugin vercel-labs-portlessThis skill uses the workspace's default tool permissions.
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.
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
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 |
|---|---|---|---|
| Allowed | Rejected | Not in their bundled PSL | |
| Apple | Rejected | Rejected | No localhost at all |
| Microsoft | Allowed | Allowed | Permissive localhost handling |
| 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 subdomain of a domain you control:
portless proxy start --tld dev
portless myapp.local.yourcompany 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.
https://myapp.devhttps://myapp.dev/api/auth/callback/googleGoogle 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.
https://myapp.dev/api/auth/callback/appleThe 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.
https://myapp.dev/api/auth/callback/azure-adMicrosoft 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.
https://myapp.dev/api/auth/callback/facebookFacebook requires each redirect URI to be registered exactly (no wildcards). Strict Mode (enabled by default) enforces exact matching.
https://myapp.dev/api/auth/callback/githubGitHub 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`;
The redirect URI sent during the OAuth flow doesn't match what's registered with the provider. Check:
NEXTAUTH_URL or equivalent is set to the portless URL (not localhost)portless list to verify).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 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.
The auth library is constructing the callback URL from localhost instead of the portless domain. Set the appropriate environment variable:
NEXTAUTH_URL=https://myapp.devAUTH_URL=https://myapp.devPORTLESS_URL is injected automatically; use it as the base URLSee examples/google-oauth for a complete working example with Next.js + NextAuth + Google OAuth using --tld dev.