[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-vercel-labs-oauth":3,"mdc-ketpz6-key":32,"related-repo-vercel-labs-oauth":1497,"related-org-vercel-labs-oauth":1515},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":21,"repoUrl":22,"updatedAt":23,"license":24,"forks":25,"topics":26,"repo":27,"sourceUrl":30,"mdContent":31},"oauth","configure OAuth for local development","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\u002FApple\u002FMicrosoft sign-in fails locally\", \"configure OAuth for local dev\", or any task involving OAuth callback URLs with portless domains.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"vercel-labs","Vercel Labs","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fvercel-labs.png",[12,16,19],{"name":13,"slug":14,"type":15},"Auth","auth","tag",{"name":17,"slug":18,"type":15},"Local Development","local-development",{"name":20,"slug":4,"type":15},"OAuth",10096,"https:\u002F\u002Fgithub.com\u002Fvercel-labs\u002Fportless","2026-07-23T05:42:06.801026",null,327,[],{"repoUrl":22,"stars":21,"forks":25,"topics":28,"description":29},[],"Replace port numbers with stable, named local URLs. For humans and agents.","https:\u002F\u002Fgithub.com\u002Fvercel-labs\u002Fportless\u002Ftree\u002FHEAD\u002Fskills\u002Foauth","---\nname: oauth\ndescription: 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\u002FApple\u002FMicrosoft sign-in fails locally\", \"configure OAuth for local dev\", or any task involving OAuth callback URLs with portless domains.\n---\n\n# OAuth with Portless\n\nOAuth 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.\n\n## The Problem\n\nWhen portless uses the default `.localhost` TLD, OAuth providers reject redirect URIs like `http:\u002F\u002Fmyapp.localhost:1355\u002Fcallback`:\n\n| Provider  | `localhost` | `.localhost` subdomains | Reason                         |\n| --------- | ----------- | ----------------------- | ------------------------------ |\n| Google    | Allowed     | Rejected                | Not in their bundled PSL       |\n| Apple     | Rejected    | Rejected                | No localhost at all            |\n| Microsoft | Allowed     | Allowed                 | Permissive localhost handling  |\n| Facebook  | Allowed     | Varies                  | Must register each URI exactly |\n| GitHub    | Allowed     | Allowed                 | Permissive                     |\n\nGoogle and Apple are the strictest. Microsoft and GitHub are more lenient with localhost.\n\n## The Fix\n\nUse a valid TLD so the redirect URI passes provider validation:\n\n```bash\nportless proxy start --tld dev\nportless myapp next dev\n# -> https:\u002F\u002Fmyapp.dev\n```\n\nAny TLD in the Public Suffix List works: `.dev`, `.app`, `.com`, `.io`, etc.\n\n### Use a domain you own\n\nBare 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:\n\n```bash\nportless proxy start --tld local.yourcompany.dev\nportless myapp next dev\n# -> https:\u002F\u002Fmyapp.local.yourcompany.dev\n```\n\nThis 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 `\u002Fetc\u002Fhosts`, and every developer shares the same redirect URIs in the provider console.\n\n## Provider Setup\n\n### Google\n\n1. Go to [Google Cloud Console > Credentials](https:\u002F\u002Fconsole.cloud.google.com\u002Fapis\u002Fcredentials)\n2. Create or edit an OAuth 2.0 Client ID (Web application)\n3. Add the portless domain to **Authorized JavaScript origins**: `https:\u002F\u002Fmyapp.dev`\n4. Add the callback to **Authorized redirect URIs**: `https:\u002F\u002Fmyapp.dev\u002Fapi\u002Fauth\u002Fcallback\u002Fgoogle`\n\nGoogle 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.\n\nHTTPS is required for `.dev` and `.app` (HSTS-preloaded). Portless handles this automatically with `--https`.\n\n### Apple\n\nApple Sign In does not allow `localhost` or IP addresses at all.\n\n1. Go to [Apple Developer > Certificates, Identifiers & Profiles](https:\u002F\u002Fdeveloper.apple.com\u002Faccount\u002Fresources)\n2. Register a Services ID\n3. Configure Sign In with Apple, adding the portless domain as a **Return URL**: `https:\u002F\u002Fmyapp.dev\u002Fapi\u002Fauth\u002Fcallback\u002Fapple`\n\nThe 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.\n\n### Microsoft (Entra \u002F Azure AD)\n\n1. Go to [Azure Portal > App registrations](https:\u002F\u002Fportal.azure.com\u002F#view\u002FMicrosoft_AAD_RegisteredApps)\n2. Create or edit an app registration\n3. Under **Authentication**, add a **Web** redirect URI: `https:\u002F\u002Fmyapp.dev\u002Fapi\u002Fauth\u002Fcallback\u002Fazure-ad`\n\nMicrosoft allows `http:\u002F\u002Flocalhost` 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.\n\n### Facebook (Meta)\n\n1. Go to [Meta for Developers > App Dashboard](https:\u002F\u002Fdevelopers.facebook.com\u002Fapps\u002F)\n2. Under **Facebook Login > Settings**, add the portless URL to **Valid OAuth Redirect URIs**: `https:\u002F\u002Fmyapp.dev\u002Fapi\u002Fauth\u002Fcallback\u002Ffacebook`\n\nFacebook requires each redirect URI to be registered exactly (no wildcards). Strict Mode (enabled by default) enforces exact matching.\n\n### GitHub\n\n1. Go to [GitHub Developer Settings > OAuth Apps](https:\u002F\u002Fgithub.com\u002Fsettings\u002Fdevelopers)\n2. Set **Authorization callback URL**: `https:\u002F\u002Fmyapp.dev\u002Fapi\u002Fauth\u002Fcallback\u002Fgithub`\n\nGitHub is permissive with localhost and subdomains. A custom TLD is not strictly required but keeps the setup consistent.\n\n## Auth Library Configuration\n\n### NextAuth \u002F Auth.js\n\nSet `NEXTAUTH_URL` to match the portless domain:\n\n```env\nNEXTAUTH_URL=https:\u002F\u002Fmyapp.dev\n```\n\nNextAuth uses this to construct callback URLs. Without it, callbacks may use `localhost` and cause a mismatch.\n\n### Passport.js\n\nSet the `callbackURL` in each strategy to use the portless domain:\n\n```js\nnew GoogleStrategy({\n  clientID: process.env.GOOGLE_CLIENT_ID,\n  clientSecret: process.env.GOOGLE_CLIENT_SECRET,\n  callbackURL: process.env.BASE_URL + \"\u002Fauth\u002Fgoogle\u002Fcallback\",\n});\n```\n\nSet `BASE_URL=https:\u002F\u002Fmyapp.dev` in your environment.\n\n### Generic \u002F Manual\n\nRead the `PORTLESS_URL` environment variable that portless injects into the child process:\n\n```js\nconst baseUrl = process.env.PORTLESS_URL || \"http:\u002F\u002Flocalhost:3000\";\nconst callbackUrl = `${baseUrl}\u002Fauth\u002Fcallback`;\n```\n\n## Troubleshooting\n\n### \"redirect_uri_mismatch\" or \"invalid redirect URI\"\n\nThe redirect URI sent during the OAuth flow doesn't match what's registered with the provider. Check:\n\n1. The provider's registered redirect URI matches the portless domain exactly (protocol, host, path)\n2. `NEXTAUTH_URL` or equivalent is set to the portless URL (not `localhost`)\n3. The proxy is running with the correct TLD (`portless list` to verify)\n\n### Provider requires HTTPS\n\n`.dev` and `.app` TLDs are HSTS-preloaded, so browsers force HTTPS. Start the proxy:\n\n```bash\nportless proxy start --tld dev\n```\n\nPortless 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.\n\n### Apple rejects the domain\n\nApple may require the domain to resolve publicly. Add a DNS A record for your dev subdomain pointing to `127.0.0.1`:\n\n```\nmyapp.local.yourcompany.dev  A  127.0.0.1\n```\n\nOr use a wildcard: `*.local.yourcompany.dev  A  127.0.0.1`.\n\n### Callback goes to wrong URL after sign-in\n\nThe auth library is constructing the callback URL from `localhost` instead of the portless domain. Set the appropriate environment variable:\n\n- **NextAuth**: `NEXTAUTH_URL=https:\u002F\u002Fmyapp.dev`\n- **Auth.js v5**: `AUTH_URL=https:\u002F\u002Fmyapp.dev`\n- **Manual**: `PORTLESS_URL` is injected automatically; use it as the base URL\n\n## Example\n\nSee [`examples\u002Fgoogle-oauth`](..\u002F..\u002Fexamples\u002Fgoogle-oauth) for a complete working example with Next.js + NextAuth + Google OAuth using `--tld dev`.\n",{"data":33,"body":34},{"name":4,"description":6},{"type":35,"children":36},"root",[37,46,69,76,96,251,256,262,267,343,378,385,405,463,484,490,495,555,586,613,618,630,666,671,677,721,741,747,784,789,794,825,830,836,842,854,870,882,888,901,1090,1102,1108,1121,1234,1240,1246,1251,1288,1294,1310,1340,1353,1359,1371,1381,1393,1399,1411,1461,1467,1491],{"type":38,"tag":39,"props":40,"children":42},"element","h1",{"id":41},"oauth-with-portless",[43],{"type":44,"value":45},"text","OAuth with Portless",{"type":38,"tag":47,"props":48,"children":49},"p",{},[50,52,59,61,67],{"type":44,"value":51},"OAuth providers validate redirect URIs against domain rules. ",{"type":38,"tag":53,"props":54,"children":56},"code",{"className":55},[],[57],{"type":44,"value":58},".localhost",{"type":44,"value":60}," subdomains fail on most providers because they are not in the Public Suffix List or are explicitly blocked. Portless fixes this with ",{"type":38,"tag":53,"props":62,"children":64},{"className":63},[],[65],{"type":44,"value":66},"--tld",{"type":44,"value":68}," to serve apps on real, valid domains.",{"type":38,"tag":70,"props":71,"children":73},"h2",{"id":72},"the-problem",[74],{"type":44,"value":75},"The Problem",{"type":38,"tag":47,"props":77,"children":78},{},[79,81,86,88,94],{"type":44,"value":80},"When portless uses the default ",{"type":38,"tag":53,"props":82,"children":84},{"className":83},[],[85],{"type":44,"value":58},{"type":44,"value":87}," TLD, OAuth providers reject redirect URIs like ",{"type":38,"tag":53,"props":89,"children":91},{"className":90},[],[92],{"type":44,"value":93},"http:\u002F\u002Fmyapp.localhost:1355\u002Fcallback",{"type":44,"value":95},":",{"type":38,"tag":97,"props":98,"children":99},"table",{},[100,138],{"type":38,"tag":101,"props":102,"children":103},"thead",{},[104],{"type":38,"tag":105,"props":106,"children":107},"tr",{},[108,114,123,133],{"type":38,"tag":109,"props":110,"children":111},"th",{},[112],{"type":44,"value":113},"Provider",{"type":38,"tag":109,"props":115,"children":116},{},[117],{"type":38,"tag":53,"props":118,"children":120},{"className":119},[],[121],{"type":44,"value":122},"localhost",{"type":38,"tag":109,"props":124,"children":125},{},[126,131],{"type":38,"tag":53,"props":127,"children":129},{"className":128},[],[130],{"type":44,"value":58},{"type":44,"value":132}," subdomains",{"type":38,"tag":109,"props":134,"children":135},{},[136],{"type":44,"value":137},"Reason",{"type":38,"tag":139,"props":140,"children":141},"tbody",{},[142,166,187,208,230],{"type":38,"tag":105,"props":143,"children":144},{},[145,151,156,161],{"type":38,"tag":146,"props":147,"children":148},"td",{},[149],{"type":44,"value":150},"Google",{"type":38,"tag":146,"props":152,"children":153},{},[154],{"type":44,"value":155},"Allowed",{"type":38,"tag":146,"props":157,"children":158},{},[159],{"type":44,"value":160},"Rejected",{"type":38,"tag":146,"props":162,"children":163},{},[164],{"type":44,"value":165},"Not in their bundled PSL",{"type":38,"tag":105,"props":167,"children":168},{},[169,174,178,182],{"type":38,"tag":146,"props":170,"children":171},{},[172],{"type":44,"value":173},"Apple",{"type":38,"tag":146,"props":175,"children":176},{},[177],{"type":44,"value":160},{"type":38,"tag":146,"props":179,"children":180},{},[181],{"type":44,"value":160},{"type":38,"tag":146,"props":183,"children":184},{},[185],{"type":44,"value":186},"No localhost at all",{"type":38,"tag":105,"props":188,"children":189},{},[190,195,199,203],{"type":38,"tag":146,"props":191,"children":192},{},[193],{"type":44,"value":194},"Microsoft",{"type":38,"tag":146,"props":196,"children":197},{},[198],{"type":44,"value":155},{"type":38,"tag":146,"props":200,"children":201},{},[202],{"type":44,"value":155},{"type":38,"tag":146,"props":204,"children":205},{},[206],{"type":44,"value":207},"Permissive localhost handling",{"type":38,"tag":105,"props":209,"children":210},{},[211,216,220,225],{"type":38,"tag":146,"props":212,"children":213},{},[214],{"type":44,"value":215},"Facebook",{"type":38,"tag":146,"props":217,"children":218},{},[219],{"type":44,"value":155},{"type":38,"tag":146,"props":221,"children":222},{},[223],{"type":44,"value":224},"Varies",{"type":38,"tag":146,"props":226,"children":227},{},[228],{"type":44,"value":229},"Must register each URI exactly",{"type":38,"tag":105,"props":231,"children":232},{},[233,238,242,246],{"type":38,"tag":146,"props":234,"children":235},{},[236],{"type":44,"value":237},"GitHub",{"type":38,"tag":146,"props":239,"children":240},{},[241],{"type":44,"value":155},{"type":38,"tag":146,"props":243,"children":244},{},[245],{"type":44,"value":155},{"type":38,"tag":146,"props":247,"children":248},{},[249],{"type":44,"value":250},"Permissive",{"type":38,"tag":47,"props":252,"children":253},{},[254],{"type":44,"value":255},"Google and Apple are the strictest. Microsoft and GitHub are more lenient with localhost.",{"type":38,"tag":70,"props":257,"children":259},{"id":258},"the-fix",[260],{"type":44,"value":261},"The Fix",{"type":38,"tag":47,"props":263,"children":264},{},[265],{"type":44,"value":266},"Use a valid TLD so the redirect URI passes provider validation:",{"type":38,"tag":268,"props":269,"children":274},"pre",{"className":270,"code":271,"language":272,"meta":273,"style":273},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","portless proxy start --tld dev\nportless myapp next dev\n# -> https:\u002F\u002Fmyapp.dev\n","bash","",[275],{"type":38,"tag":53,"props":276,"children":277},{"__ignoreMap":273},[278,311,333],{"type":38,"tag":279,"props":280,"children":283},"span",{"class":281,"line":282},"line",1,[284,290,296,301,306],{"type":38,"tag":279,"props":285,"children":287},{"style":286},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[288],{"type":44,"value":289},"portless",{"type":38,"tag":279,"props":291,"children":293},{"style":292},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[294],{"type":44,"value":295}," proxy",{"type":38,"tag":279,"props":297,"children":298},{"style":292},[299],{"type":44,"value":300}," start",{"type":38,"tag":279,"props":302,"children":303},{"style":292},[304],{"type":44,"value":305}," --tld",{"type":38,"tag":279,"props":307,"children":308},{"style":292},[309],{"type":44,"value":310}," dev\n",{"type":38,"tag":279,"props":312,"children":314},{"class":281,"line":313},2,[315,319,324,329],{"type":38,"tag":279,"props":316,"children":317},{"style":286},[318],{"type":44,"value":289},{"type":38,"tag":279,"props":320,"children":321},{"style":292},[322],{"type":44,"value":323}," myapp",{"type":38,"tag":279,"props":325,"children":326},{"style":292},[327],{"type":44,"value":328}," next",{"type":38,"tag":279,"props":330,"children":331},{"style":292},[332],{"type":44,"value":310},{"type":38,"tag":279,"props":334,"children":336},{"class":281,"line":335},3,[337],{"type":38,"tag":279,"props":338,"children":340},{"style":339},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic",[341],{"type":44,"value":342},"# -> https:\u002F\u002Fmyapp.dev\n",{"type":38,"tag":47,"props":344,"children":345},{},[346,348,354,356,362,363,369,370,376],{"type":44,"value":347},"Any TLD in the Public Suffix List works: ",{"type":38,"tag":53,"props":349,"children":351},{"className":350},[],[352],{"type":44,"value":353},".dev",{"type":44,"value":355},", ",{"type":38,"tag":53,"props":357,"children":359},{"className":358},[],[360],{"type":44,"value":361},".app",{"type":44,"value":355},{"type":38,"tag":53,"props":364,"children":366},{"className":365},[],[367],{"type":44,"value":368},".com",{"type":44,"value":355},{"type":38,"tag":53,"props":371,"children":373},{"className":372},[],[374],{"type":44,"value":375},".io",{"type":44,"value":377},", etc.",{"type":38,"tag":379,"props":380,"children":382},"h3",{"id":381},"use-a-domain-you-own",[383],{"type":44,"value":384},"Use a domain you own",{"type":38,"tag":47,"props":386,"children":387},{},[388,390,395,397,403],{"type":44,"value":389},"Bare TLDs like ",{"type":38,"tag":53,"props":391,"children":393},{"className":392},[],[394],{"type":44,"value":353},{"type":44,"value":396}," mean ",{"type":38,"tag":53,"props":398,"children":400},{"className":399},[],[401],{"type":44,"value":402},"myapp.dev",{"type":44,"value":404}," 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:",{"type":38,"tag":268,"props":406,"children":408},{"className":270,"code":407,"language":272,"meta":273,"style":273},"portless proxy start --tld local.yourcompany.dev\nportless myapp next dev\n# -> https:\u002F\u002Fmyapp.local.yourcompany.dev\n",[409],{"type":38,"tag":53,"props":410,"children":411},{"__ignoreMap":273},[412,436,455],{"type":38,"tag":279,"props":413,"children":414},{"class":281,"line":282},[415,419,423,427,431],{"type":38,"tag":279,"props":416,"children":417},{"style":286},[418],{"type":44,"value":289},{"type":38,"tag":279,"props":420,"children":421},{"style":292},[422],{"type":44,"value":295},{"type":38,"tag":279,"props":424,"children":425},{"style":292},[426],{"type":44,"value":300},{"type":38,"tag":279,"props":428,"children":429},{"style":292},[430],{"type":44,"value":305},{"type":38,"tag":279,"props":432,"children":433},{"style":292},[434],{"type":44,"value":435}," local.yourcompany.dev\n",{"type":38,"tag":279,"props":437,"children":438},{"class":281,"line":313},[439,443,447,451],{"type":38,"tag":279,"props":440,"children":441},{"style":286},[442],{"type":44,"value":289},{"type":38,"tag":279,"props":444,"children":445},{"style":292},[446],{"type":44,"value":323},{"type":38,"tag":279,"props":448,"children":449},{"style":292},[450],{"type":44,"value":328},{"type":38,"tag":279,"props":452,"children":453},{"style":292},[454],{"type":44,"value":310},{"type":38,"tag":279,"props":456,"children":457},{"class":281,"line":335},[458],{"type":38,"tag":279,"props":459,"children":460},{"style":339},[461],{"type":44,"value":462},"# -> https:\u002F\u002Fmyapp.local.yourcompany.dev\n",{"type":38,"tag":47,"props":464,"children":465},{},[466,468,474,476,482],{"type":44,"value":467},"This ensures no outbound traffic reaches something you don't own. For teams, set a wildcard DNS record (",{"type":38,"tag":53,"props":469,"children":471},{"className":470},[],[472],{"type":44,"value":473},"*.local.yourcompany.dev -> 127.0.0.1",{"type":44,"value":475},") so every developer gets resolution without ",{"type":38,"tag":53,"props":477,"children":479},{"className":478},[],[480],{"type":44,"value":481},"\u002Fetc\u002Fhosts",{"type":44,"value":483},", and every developer shares the same redirect URIs in the provider console.",{"type":38,"tag":70,"props":485,"children":487},{"id":486},"provider-setup",[488],{"type":44,"value":489},"Provider Setup",{"type":38,"tag":379,"props":491,"children":493},{"id":492},"google",[494],{"type":44,"value":150},{"type":38,"tag":496,"props":497,"children":498},"ol",{},[499,514,519,538],{"type":38,"tag":500,"props":501,"children":502},"li",{},[503,505],{"type":44,"value":504},"Go to ",{"type":38,"tag":506,"props":507,"children":511},"a",{"href":508,"rel":509},"https:\u002F\u002Fconsole.cloud.google.com\u002Fapis\u002Fcredentials",[510],"nofollow",[512],{"type":44,"value":513},"Google Cloud Console > Credentials",{"type":38,"tag":500,"props":515,"children":516},{},[517],{"type":44,"value":518},"Create or edit an OAuth 2.0 Client ID (Web application)",{"type":38,"tag":500,"props":520,"children":521},{},[522,524,530,532],{"type":44,"value":523},"Add the portless domain to ",{"type":38,"tag":525,"props":526,"children":527},"strong",{},[528],{"type":44,"value":529},"Authorized JavaScript origins",{"type":44,"value":531},": ",{"type":38,"tag":53,"props":533,"children":535},{"className":534},[],[536],{"type":44,"value":537},"https:\u002F\u002Fmyapp.dev",{"type":38,"tag":500,"props":539,"children":540},{},[541,543,548,549],{"type":44,"value":542},"Add the callback to ",{"type":38,"tag":525,"props":544,"children":545},{},[546],{"type":44,"value":547},"Authorized redirect URIs",{"type":44,"value":531},{"type":38,"tag":53,"props":550,"children":552},{"className":551},[],[553],{"type":44,"value":554},"https:\u002F\u002Fmyapp.dev\u002Fapi\u002Fauth\u002Fcallback\u002Fgoogle",{"type":38,"tag":47,"props":556,"children":557},{},[558,560,565,567,572,573,578,579,584],{"type":44,"value":559},"Google validates domains against the Public Suffix List. The domain must end with a recognized TLD. ",{"type":38,"tag":53,"props":561,"children":563},{"className":562},[],[564],{"type":44,"value":58},{"type":44,"value":566}," subdomains fail this check; ",{"type":38,"tag":53,"props":568,"children":570},{"className":569},[],[571],{"type":44,"value":353},{"type":44,"value":355},{"type":38,"tag":53,"props":574,"children":576},{"className":575},[],[577],{"type":44,"value":361},{"type":44,"value":355},{"type":38,"tag":53,"props":580,"children":582},{"className":581},[],[583],{"type":44,"value":368},{"type":44,"value":585},", etc. all pass.",{"type":38,"tag":47,"props":587,"children":588},{},[589,591,596,598,603,605,611],{"type":44,"value":590},"HTTPS is required for ",{"type":38,"tag":53,"props":592,"children":594},{"className":593},[],[595],{"type":44,"value":353},{"type":44,"value":597}," and ",{"type":38,"tag":53,"props":599,"children":601},{"className":600},[],[602],{"type":44,"value":361},{"type":44,"value":604}," (HSTS-preloaded). Portless handles this automatically with ",{"type":38,"tag":53,"props":606,"children":608},{"className":607},[],[609],{"type":44,"value":610},"--https",{"type":44,"value":612},".",{"type":38,"tag":379,"props":614,"children":616},{"id":615},"apple",[617],{"type":44,"value":173},{"type":38,"tag":47,"props":619,"children":620},{},[621,623,628],{"type":44,"value":622},"Apple Sign In does not allow ",{"type":38,"tag":53,"props":624,"children":626},{"className":625},[],[627],{"type":44,"value":122},{"type":44,"value":629}," or IP addresses at all.",{"type":38,"tag":496,"props":631,"children":632},{},[633,644,649],{"type":38,"tag":500,"props":634,"children":635},{},[636,637],{"type":44,"value":504},{"type":38,"tag":506,"props":638,"children":641},{"href":639,"rel":640},"https:\u002F\u002Fdeveloper.apple.com\u002Faccount\u002Fresources",[510],[642],{"type":44,"value":643},"Apple Developer > Certificates, Identifiers & Profiles",{"type":38,"tag":500,"props":645,"children":646},{},[647],{"type":44,"value":648},"Register a Services ID",{"type":38,"tag":500,"props":650,"children":651},{},[652,654,659,660],{"type":44,"value":653},"Configure Sign In with Apple, adding the portless domain as a ",{"type":38,"tag":525,"props":655,"children":656},{},[657],{"type":44,"value":658},"Return URL",{"type":44,"value":531},{"type":38,"tag":53,"props":661,"children":663},{"className":662},[],[664],{"type":44,"value":665},"https:\u002F\u002Fmyapp.dev\u002Fapi\u002Fauth\u002Fcallback\u002Fapple",{"type":38,"tag":47,"props":667,"children":668},{},[669],{"type":44,"value":670},"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.",{"type":38,"tag":379,"props":672,"children":674},{"id":673},"microsoft-entra-azure-ad",[675],{"type":44,"value":676},"Microsoft (Entra \u002F Azure AD)",{"type":38,"tag":496,"props":678,"children":679},{},[680,691,696],{"type":38,"tag":500,"props":681,"children":682},{},[683,684],{"type":44,"value":504},{"type":38,"tag":506,"props":685,"children":688},{"href":686,"rel":687},"https:\u002F\u002Fportal.azure.com\u002F#view\u002FMicrosoft_AAD_RegisteredApps",[510],[689],{"type":44,"value":690},"Azure Portal > App registrations",{"type":38,"tag":500,"props":692,"children":693},{},[694],{"type":44,"value":695},"Create or edit an app registration",{"type":38,"tag":500,"props":697,"children":698},{},[699,701,706,708,713,715],{"type":44,"value":700},"Under ",{"type":38,"tag":525,"props":702,"children":703},{},[704],{"type":44,"value":705},"Authentication",{"type":44,"value":707},", add a ",{"type":38,"tag":525,"props":709,"children":710},{},[711],{"type":44,"value":712},"Web",{"type":44,"value":714}," redirect URI: ",{"type":38,"tag":53,"props":716,"children":718},{"className":717},[],[719],{"type":44,"value":720},"https:\u002F\u002Fmyapp.dev\u002Fapi\u002Fauth\u002Fcallback\u002Fazure-ad",{"type":38,"tag":47,"props":722,"children":723},{},[724,726,732,734,739],{"type":44,"value":725},"Microsoft allows ",{"type":38,"tag":53,"props":727,"children":729},{"className":728},[],[730],{"type":44,"value":731},"http:\u002F\u002Flocalhost",{"type":44,"value":733}," with any port for development. It also accepts ",{"type":38,"tag":53,"props":735,"children":737},{"className":736},[],[738],{"type":44,"value":58},{"type":44,"value":740}," subdomains in most cases. Using a custom TLD with portless is still recommended for consistency across providers.",{"type":38,"tag":379,"props":742,"children":744},{"id":743},"facebook-meta",[745],{"type":44,"value":746},"Facebook (Meta)",{"type":38,"tag":496,"props":748,"children":749},{},[750,761],{"type":38,"tag":500,"props":751,"children":752},{},[753,754],{"type":44,"value":504},{"type":38,"tag":506,"props":755,"children":758},{"href":756,"rel":757},"https:\u002F\u002Fdevelopers.facebook.com\u002Fapps\u002F",[510],[759],{"type":44,"value":760},"Meta for Developers > App Dashboard",{"type":38,"tag":500,"props":762,"children":763},{},[764,765,770,772,777,778],{"type":44,"value":700},{"type":38,"tag":525,"props":766,"children":767},{},[768],{"type":44,"value":769},"Facebook Login > Settings",{"type":44,"value":771},", add the portless URL to ",{"type":38,"tag":525,"props":773,"children":774},{},[775],{"type":44,"value":776},"Valid OAuth Redirect URIs",{"type":44,"value":531},{"type":38,"tag":53,"props":779,"children":781},{"className":780},[],[782],{"type":44,"value":783},"https:\u002F\u002Fmyapp.dev\u002Fapi\u002Fauth\u002Fcallback\u002Ffacebook",{"type":38,"tag":47,"props":785,"children":786},{},[787],{"type":44,"value":788},"Facebook requires each redirect URI to be registered exactly (no wildcards). Strict Mode (enabled by default) enforces exact matching.",{"type":38,"tag":379,"props":790,"children":792},{"id":791},"github",[793],{"type":44,"value":237},{"type":38,"tag":496,"props":795,"children":796},{},[797,808],{"type":38,"tag":500,"props":798,"children":799},{},[800,801],{"type":44,"value":504},{"type":38,"tag":506,"props":802,"children":805},{"href":803,"rel":804},"https:\u002F\u002Fgithub.com\u002Fsettings\u002Fdevelopers",[510],[806],{"type":44,"value":807},"GitHub Developer Settings > OAuth Apps",{"type":38,"tag":500,"props":809,"children":810},{},[811,813,818,819],{"type":44,"value":812},"Set ",{"type":38,"tag":525,"props":814,"children":815},{},[816],{"type":44,"value":817},"Authorization callback URL",{"type":44,"value":531},{"type":38,"tag":53,"props":820,"children":822},{"className":821},[],[823],{"type":44,"value":824},"https:\u002F\u002Fmyapp.dev\u002Fapi\u002Fauth\u002Fcallback\u002Fgithub",{"type":38,"tag":47,"props":826,"children":827},{},[828],{"type":44,"value":829},"GitHub is permissive with localhost and subdomains. A custom TLD is not strictly required but keeps the setup consistent.",{"type":38,"tag":70,"props":831,"children":833},{"id":832},"auth-library-configuration",[834],{"type":44,"value":835},"Auth Library Configuration",{"type":38,"tag":379,"props":837,"children":839},{"id":838},"nextauth-authjs",[840],{"type":44,"value":841},"NextAuth \u002F Auth.js",{"type":38,"tag":47,"props":843,"children":844},{},[845,846,852],{"type":44,"value":812},{"type":38,"tag":53,"props":847,"children":849},{"className":848},[],[850],{"type":44,"value":851},"NEXTAUTH_URL",{"type":44,"value":853}," to match the portless domain:",{"type":38,"tag":268,"props":855,"children":859},{"className":856,"code":857,"language":858,"meta":273,"style":273},"language-env shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","NEXTAUTH_URL=https:\u002F\u002Fmyapp.dev\n","env",[860],{"type":38,"tag":53,"props":861,"children":862},{"__ignoreMap":273},[863],{"type":38,"tag":279,"props":864,"children":865},{"class":281,"line":282},[866],{"type":38,"tag":279,"props":867,"children":868},{},[869],{"type":44,"value":857},{"type":38,"tag":47,"props":871,"children":872},{},[873,875,880],{"type":44,"value":874},"NextAuth uses this to construct callback URLs. Without it, callbacks may use ",{"type":38,"tag":53,"props":876,"children":878},{"className":877},[],[879],{"type":44,"value":122},{"type":44,"value":881}," and cause a mismatch.",{"type":38,"tag":379,"props":883,"children":885},{"id":884},"passportjs",[886],{"type":44,"value":887},"Passport.js",{"type":38,"tag":47,"props":889,"children":890},{},[891,893,899],{"type":44,"value":892},"Set the ",{"type":38,"tag":53,"props":894,"children":896},{"className":895},[],[897],{"type":44,"value":898},"callbackURL",{"type":44,"value":900}," in each strategy to use the portless domain:",{"type":38,"tag":268,"props":902,"children":906},{"className":903,"code":904,"language":905,"meta":273,"style":273},"language-js shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","new GoogleStrategy({\n  clientID: process.env.GOOGLE_CLIENT_ID,\n  clientSecret: process.env.GOOGLE_CLIENT_SECRET,\n  callbackURL: process.env.BASE_URL + \"\u002Fauth\u002Fgoogle\u002Fcallback\",\n});\n","js",[907],{"type":38,"tag":53,"props":908,"children":909},{"__ignoreMap":273},[910,936,976,1013,1071],{"type":38,"tag":279,"props":911,"children":912},{"class":281,"line":282},[913,919,925,931],{"type":38,"tag":279,"props":914,"children":916},{"style":915},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[917],{"type":44,"value":918},"new",{"type":38,"tag":279,"props":920,"children":922},{"style":921},"--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF",[923],{"type":44,"value":924}," GoogleStrategy",{"type":38,"tag":279,"props":926,"children":928},{"style":927},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[929],{"type":44,"value":930},"(",{"type":38,"tag":279,"props":932,"children":933},{"style":915},[934],{"type":44,"value":935},"{\n",{"type":38,"tag":279,"props":937,"children":938},{"class":281,"line":313},[939,945,949,954,958,962,966,971],{"type":38,"tag":279,"props":940,"children":942},{"style":941},"--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178",[943],{"type":44,"value":944},"  clientID",{"type":38,"tag":279,"props":946,"children":947},{"style":915},[948],{"type":44,"value":95},{"type":38,"tag":279,"props":950,"children":951},{"style":927},[952],{"type":44,"value":953}," process",{"type":38,"tag":279,"props":955,"children":956},{"style":915},[957],{"type":44,"value":612},{"type":38,"tag":279,"props":959,"children":960},{"style":927},[961],{"type":44,"value":858},{"type":38,"tag":279,"props":963,"children":964},{"style":915},[965],{"type":44,"value":612},{"type":38,"tag":279,"props":967,"children":968},{"style":927},[969],{"type":44,"value":970},"GOOGLE_CLIENT_ID",{"type":38,"tag":279,"props":972,"children":973},{"style":915},[974],{"type":44,"value":975},",\n",{"type":38,"tag":279,"props":977,"children":978},{"class":281,"line":335},[979,984,988,992,996,1000,1004,1009],{"type":38,"tag":279,"props":980,"children":981},{"style":941},[982],{"type":44,"value":983},"  clientSecret",{"type":38,"tag":279,"props":985,"children":986},{"style":915},[987],{"type":44,"value":95},{"type":38,"tag":279,"props":989,"children":990},{"style":927},[991],{"type":44,"value":953},{"type":38,"tag":279,"props":993,"children":994},{"style":915},[995],{"type":44,"value":612},{"type":38,"tag":279,"props":997,"children":998},{"style":927},[999],{"type":44,"value":858},{"type":38,"tag":279,"props":1001,"children":1002},{"style":915},[1003],{"type":44,"value":612},{"type":38,"tag":279,"props":1005,"children":1006},{"style":927},[1007],{"type":44,"value":1008},"GOOGLE_CLIENT_SECRET",{"type":38,"tag":279,"props":1010,"children":1011},{"style":915},[1012],{"type":44,"value":975},{"type":38,"tag":279,"props":1014,"children":1016},{"class":281,"line":1015},4,[1017,1022,1026,1030,1034,1038,1042,1047,1052,1057,1062,1067],{"type":38,"tag":279,"props":1018,"children":1019},{"style":941},[1020],{"type":44,"value":1021},"  callbackURL",{"type":38,"tag":279,"props":1023,"children":1024},{"style":915},[1025],{"type":44,"value":95},{"type":38,"tag":279,"props":1027,"children":1028},{"style":927},[1029],{"type":44,"value":953},{"type":38,"tag":279,"props":1031,"children":1032},{"style":915},[1033],{"type":44,"value":612},{"type":38,"tag":279,"props":1035,"children":1036},{"style":927},[1037],{"type":44,"value":858},{"type":38,"tag":279,"props":1039,"children":1040},{"style":915},[1041],{"type":44,"value":612},{"type":38,"tag":279,"props":1043,"children":1044},{"style":927},[1045],{"type":44,"value":1046},"BASE_URL ",{"type":38,"tag":279,"props":1048,"children":1049},{"style":915},[1050],{"type":44,"value":1051},"+",{"type":38,"tag":279,"props":1053,"children":1054},{"style":915},[1055],{"type":44,"value":1056}," \"",{"type":38,"tag":279,"props":1058,"children":1059},{"style":292},[1060],{"type":44,"value":1061},"\u002Fauth\u002Fgoogle\u002Fcallback",{"type":38,"tag":279,"props":1063,"children":1064},{"style":915},[1065],{"type":44,"value":1066},"\"",{"type":38,"tag":279,"props":1068,"children":1069},{"style":915},[1070],{"type":44,"value":975},{"type":38,"tag":279,"props":1072,"children":1074},{"class":281,"line":1073},5,[1075,1080,1085],{"type":38,"tag":279,"props":1076,"children":1077},{"style":915},[1078],{"type":44,"value":1079},"}",{"type":38,"tag":279,"props":1081,"children":1082},{"style":927},[1083],{"type":44,"value":1084},")",{"type":38,"tag":279,"props":1086,"children":1087},{"style":915},[1088],{"type":44,"value":1089},";\n",{"type":38,"tag":47,"props":1091,"children":1092},{},[1093,1094,1100],{"type":44,"value":812},{"type":38,"tag":53,"props":1095,"children":1097},{"className":1096},[],[1098],{"type":44,"value":1099},"BASE_URL=https:\u002F\u002Fmyapp.dev",{"type":44,"value":1101}," in your environment.",{"type":38,"tag":379,"props":1103,"children":1105},{"id":1104},"generic-manual",[1106],{"type":44,"value":1107},"Generic \u002F Manual",{"type":38,"tag":47,"props":1109,"children":1110},{},[1111,1113,1119],{"type":44,"value":1112},"Read the ",{"type":38,"tag":53,"props":1114,"children":1116},{"className":1115},[],[1117],{"type":44,"value":1118},"PORTLESS_URL",{"type":44,"value":1120}," environment variable that portless injects into the child process:",{"type":38,"tag":268,"props":1122,"children":1124},{"className":903,"code":1123,"language":905,"meta":273,"style":273},"const baseUrl = process.env.PORTLESS_URL || \"http:\u002F\u002Flocalhost:3000\";\nconst callbackUrl = `${baseUrl}\u002Fauth\u002Fcallback`;\n",[1125],{"type":38,"tag":53,"props":1126,"children":1127},{"__ignoreMap":273},[1128,1190],{"type":38,"tag":279,"props":1129,"children":1130},{"class":281,"line":282},[1131,1137,1142,1147,1151,1155,1159,1163,1168,1173,1177,1182,1186],{"type":38,"tag":279,"props":1132,"children":1134},{"style":1133},"--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA",[1135],{"type":44,"value":1136},"const",{"type":38,"tag":279,"props":1138,"children":1139},{"style":927},[1140],{"type":44,"value":1141}," baseUrl ",{"type":38,"tag":279,"props":1143,"children":1144},{"style":915},[1145],{"type":44,"value":1146},"=",{"type":38,"tag":279,"props":1148,"children":1149},{"style":927},[1150],{"type":44,"value":953},{"type":38,"tag":279,"props":1152,"children":1153},{"style":915},[1154],{"type":44,"value":612},{"type":38,"tag":279,"props":1156,"children":1157},{"style":927},[1158],{"type":44,"value":858},{"type":38,"tag":279,"props":1160,"children":1161},{"style":915},[1162],{"type":44,"value":612},{"type":38,"tag":279,"props":1164,"children":1165},{"style":927},[1166],{"type":44,"value":1167},"PORTLESS_URL ",{"type":38,"tag":279,"props":1169,"children":1170},{"style":915},[1171],{"type":44,"value":1172},"||",{"type":38,"tag":279,"props":1174,"children":1175},{"style":915},[1176],{"type":44,"value":1056},{"type":38,"tag":279,"props":1178,"children":1179},{"style":292},[1180],{"type":44,"value":1181},"http:\u002F\u002Flocalhost:3000",{"type":38,"tag":279,"props":1183,"children":1184},{"style":915},[1185],{"type":44,"value":1066},{"type":38,"tag":279,"props":1187,"children":1188},{"style":915},[1189],{"type":44,"value":1089},{"type":38,"tag":279,"props":1191,"children":1192},{"class":281,"line":313},[1193,1197,1202,1206,1211,1216,1220,1225,1230],{"type":38,"tag":279,"props":1194,"children":1195},{"style":1133},[1196],{"type":44,"value":1136},{"type":38,"tag":279,"props":1198,"children":1199},{"style":927},[1200],{"type":44,"value":1201}," callbackUrl ",{"type":38,"tag":279,"props":1203,"children":1204},{"style":915},[1205],{"type":44,"value":1146},{"type":38,"tag":279,"props":1207,"children":1208},{"style":915},[1209],{"type":44,"value":1210}," `${",{"type":38,"tag":279,"props":1212,"children":1213},{"style":927},[1214],{"type":44,"value":1215},"baseUrl",{"type":38,"tag":279,"props":1217,"children":1218},{"style":915},[1219],{"type":44,"value":1079},{"type":38,"tag":279,"props":1221,"children":1222},{"style":292},[1223],{"type":44,"value":1224},"\u002Fauth\u002Fcallback",{"type":38,"tag":279,"props":1226,"children":1227},{"style":915},[1228],{"type":44,"value":1229},"`",{"type":38,"tag":279,"props":1231,"children":1232},{"style":915},[1233],{"type":44,"value":1089},{"type":38,"tag":70,"props":1235,"children":1237},{"id":1236},"troubleshooting",[1238],{"type":44,"value":1239},"Troubleshooting",{"type":38,"tag":379,"props":1241,"children":1243},{"id":1242},"redirect_uri_mismatch-or-invalid-redirect-uri",[1244],{"type":44,"value":1245},"\"redirect_uri_mismatch\" or \"invalid redirect URI\"",{"type":38,"tag":47,"props":1247,"children":1248},{},[1249],{"type":44,"value":1250},"The redirect URI sent during the OAuth flow doesn't match what's registered with the provider. Check:",{"type":38,"tag":496,"props":1252,"children":1253},{},[1254,1259,1275],{"type":38,"tag":500,"props":1255,"children":1256},{},[1257],{"type":44,"value":1258},"The provider's registered redirect URI matches the portless domain exactly (protocol, host, path)",{"type":38,"tag":500,"props":1260,"children":1261},{},[1262,1267,1269,1274],{"type":38,"tag":53,"props":1263,"children":1265},{"className":1264},[],[1266],{"type":44,"value":851},{"type":44,"value":1268}," or equivalent is set to the portless URL (not ",{"type":38,"tag":53,"props":1270,"children":1272},{"className":1271},[],[1273],{"type":44,"value":122},{"type":44,"value":1084},{"type":38,"tag":500,"props":1276,"children":1277},{},[1278,1280,1286],{"type":44,"value":1279},"The proxy is running with the correct TLD (",{"type":38,"tag":53,"props":1281,"children":1283},{"className":1282},[],[1284],{"type":44,"value":1285},"portless list",{"type":44,"value":1287}," to verify)",{"type":38,"tag":379,"props":1289,"children":1291},{"id":1290},"provider-requires-https",[1292],{"type":44,"value":1293},"Provider requires HTTPS",{"type":38,"tag":47,"props":1295,"children":1296},{},[1297,1302,1303,1308],{"type":38,"tag":53,"props":1298,"children":1300},{"className":1299},[],[1301],{"type":44,"value":353},{"type":44,"value":597},{"type":38,"tag":53,"props":1304,"children":1306},{"className":1305},[],[1307],{"type":44,"value":361},{"type":44,"value":1309}," TLDs are HSTS-preloaded, so browsers force HTTPS. Start the proxy:",{"type":38,"tag":268,"props":1311,"children":1313},{"className":270,"code":1312,"language":272,"meta":273,"style":273},"portless proxy start --tld dev\n",[1314],{"type":38,"tag":53,"props":1315,"children":1316},{"__ignoreMap":273},[1317],{"type":38,"tag":279,"props":1318,"children":1319},{"class":281,"line":282},[1320,1324,1328,1332,1336],{"type":38,"tag":279,"props":1321,"children":1322},{"style":286},[1323],{"type":44,"value":289},{"type":38,"tag":279,"props":1325,"children":1326},{"style":292},[1327],{"type":44,"value":295},{"type":38,"tag":279,"props":1329,"children":1330},{"style":292},[1331],{"type":44,"value":300},{"type":38,"tag":279,"props":1333,"children":1334},{"style":292},[1335],{"type":44,"value":305},{"type":38,"tag":279,"props":1337,"children":1338},{"style":292},[1339],{"type":44,"value":310},{"type":38,"tag":47,"props":1341,"children":1342},{},[1343,1345,1351],{"type":44,"value":1344},"Portless defaults to HTTPS on port 443 (auto-elevates with sudo). Run ",{"type":38,"tag":53,"props":1346,"children":1348},{"className":1347},[],[1349],{"type":44,"value":1350},"portless trust",{"type":44,"value":1352}," to add the local CA to your system trust store and eliminate browser warnings.",{"type":38,"tag":379,"props":1354,"children":1356},{"id":1355},"apple-rejects-the-domain",[1357],{"type":44,"value":1358},"Apple rejects the domain",{"type":38,"tag":47,"props":1360,"children":1361},{},[1362,1364,1370],{"type":44,"value":1363},"Apple may require the domain to resolve publicly. Add a DNS A record for your dev subdomain pointing to ",{"type":38,"tag":53,"props":1365,"children":1367},{"className":1366},[],[1368],{"type":44,"value":1369},"127.0.0.1",{"type":44,"value":95},{"type":38,"tag":268,"props":1372,"children":1376},{"className":1373,"code":1375,"language":44},[1374],"language-text","myapp.local.yourcompany.dev  A  127.0.0.1\n",[1377],{"type":38,"tag":53,"props":1378,"children":1379},{"__ignoreMap":273},[1380],{"type":44,"value":1375},{"type":38,"tag":47,"props":1382,"children":1383},{},[1384,1386,1392],{"type":44,"value":1385},"Or use a wildcard: ",{"type":38,"tag":53,"props":1387,"children":1389},{"className":1388},[],[1390],{"type":44,"value":1391},"*.local.yourcompany.dev  A  127.0.0.1",{"type":44,"value":612},{"type":38,"tag":379,"props":1394,"children":1396},{"id":1395},"callback-goes-to-wrong-url-after-sign-in",[1397],{"type":44,"value":1398},"Callback goes to wrong URL after sign-in",{"type":38,"tag":47,"props":1400,"children":1401},{},[1402,1404,1409],{"type":44,"value":1403},"The auth library is constructing the callback URL from ",{"type":38,"tag":53,"props":1405,"children":1407},{"className":1406},[],[1408],{"type":44,"value":122},{"type":44,"value":1410}," instead of the portless domain. Set the appropriate environment variable:",{"type":38,"tag":1412,"props":1413,"children":1414},"ul",{},[1415,1430,1445],{"type":38,"tag":500,"props":1416,"children":1417},{},[1418,1423,1424],{"type":38,"tag":525,"props":1419,"children":1420},{},[1421],{"type":44,"value":1422},"NextAuth",{"type":44,"value":531},{"type":38,"tag":53,"props":1425,"children":1427},{"className":1426},[],[1428],{"type":44,"value":1429},"NEXTAUTH_URL=https:\u002F\u002Fmyapp.dev",{"type":38,"tag":500,"props":1431,"children":1432},{},[1433,1438,1439],{"type":38,"tag":525,"props":1434,"children":1435},{},[1436],{"type":44,"value":1437},"Auth.js v5",{"type":44,"value":531},{"type":38,"tag":53,"props":1440,"children":1442},{"className":1441},[],[1443],{"type":44,"value":1444},"AUTH_URL=https:\u002F\u002Fmyapp.dev",{"type":38,"tag":500,"props":1446,"children":1447},{},[1448,1453,1454,1459],{"type":38,"tag":525,"props":1449,"children":1450},{},[1451],{"type":44,"value":1452},"Manual",{"type":44,"value":531},{"type":38,"tag":53,"props":1455,"children":1457},{"className":1456},[],[1458],{"type":44,"value":1118},{"type":44,"value":1460}," is injected automatically; use it as the base URL",{"type":38,"tag":70,"props":1462,"children":1464},{"id":1463},"example",[1465],{"type":44,"value":1466},"Example",{"type":38,"tag":47,"props":1468,"children":1469},{},[1470,1472,1482,1484,1490],{"type":44,"value":1471},"See ",{"type":38,"tag":506,"props":1473,"children":1475},{"href":1474},"..\u002F..\u002Fexamples\u002Fgoogle-oauth",[1476],{"type":38,"tag":53,"props":1477,"children":1479},{"className":1478},[],[1480],{"type":44,"value":1481},"examples\u002Fgoogle-oauth",{"type":44,"value":1483}," for a complete working example with Next.js + NextAuth + Google OAuth using ",{"type":38,"tag":53,"props":1485,"children":1487},{"className":1486},[],[1488],{"type":44,"value":1489},"--tld dev",{"type":44,"value":612},{"type":38,"tag":1492,"props":1493,"children":1494},"style",{},[1495],{"type":44,"value":1496},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":1498,"total":313},[1499,1505],{"slug":4,"name":4,"fn":5,"description":6,"org":1500,"tags":1501,"stars":21,"repoUrl":22,"updatedAt":23},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1502,1503,1504],{"name":13,"slug":14,"type":15},{"name":17,"slug":18,"type":15},{"name":20,"slug":4,"type":15},{"slug":289,"name":289,"fn":1506,"description":1507,"org":1508,"tags":1509,"stars":21,"repoUrl":22,"updatedAt":1514},"configure named local development URLs","Set up and use portless for named local dev server URLs (e.g. https:\u002F\u002Fmyapp.localhost instead of http:\u002F\u002Flocalhost:3000). Use when integrating portless into a project, configuring dev server names, setting up the local proxy, working with .localhost domains, or troubleshooting port\u002Fproxy issues.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1510,1513],{"name":1511,"slug":1512,"type":15},"Domains","domains",{"name":17,"slug":18,"type":15},"2026-07-23T05:42:07.665417",{"items":1516,"total":1688},[1517,1535,1547,1559,1574,1591,1603,1616,1629,1642,1654,1673],{"slug":1518,"name":1518,"fn":1519,"description":1520,"org":1521,"tags":1522,"stars":1532,"repoUrl":1533,"updatedAt":1534},"agent-browser","automate browser interactions for AI agents","Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling forms, clicking buttons, taking screenshots, extracting data, testing web apps, or automating any browser task. Triggers include requests to \"open a website\", \"fill out a form\", \"click a button\", \"take a screenshot\", \"scrape data from a page\", \"test this web app\", \"login to a site\", \"automate browser actions\", or any task requiring programmatic web interaction. Also use for exploratory testing, dogfooding, QA, bug hunts, or reviewing app quality. Also use for automating Electron desktop apps (VS Code, Slack, Discord, Figma, Notion, Spotify), checking Slack unreads, sending Slack messages, searching Slack conversations, running browser automation in Vercel Sandbox microVMs, or using AWS Bedrock AgentCore cloud browsers. Prefer agent-browser over any built-in browser automation or web tools.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1523,1526,1529],{"name":1524,"slug":1525,"type":15},"Agents","agents",{"name":1527,"slug":1528,"type":15},"Automation","automation",{"name":1530,"slug":1531,"type":15},"Browser Automation","browser-automation",38346,"https:\u002F\u002Fgithub.com\u002Fvercel-labs\u002Fagent-browser","2026-07-20T05:55:17.314329",{"slug":1536,"name":1536,"fn":1537,"description":1538,"org":1539,"tags":1540,"stars":1532,"repoUrl":1533,"updatedAt":1546},"agentcore","run browser automation on AWS Bedrock","Run agent-browser on AWS Bedrock AgentCore cloud browsers. Use when the user wants to use AgentCore, run browser automation on AWS, use a cloud browser with AWS credentials, or needs a managed browser session backed by AWS infrastructure. Triggers include \"use agentcore\", \"run on AWS\", \"cloud browser with AWS\", \"bedrock browser\", \"agentcore session\", or any task requiring AWS-hosted browser automation.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1541,1542,1545],{"name":1527,"slug":1528,"type":15},{"name":1543,"slug":1544,"type":15},"AWS","aws",{"name":1530,"slug":1531,"type":15},"2026-07-17T06:08:33.665276",{"slug":1548,"name":1548,"fn":1549,"description":1550,"org":1551,"tags":1552,"stars":1532,"repoUrl":1533,"updatedAt":1558},"core","navigate and interact with web pages","Core agent-browser usage guide. Read this before running any agent-browser commands. Covers the snapshot-and-ref workflow, navigating pages, interacting with elements (click, fill, type, select), extracting text and data, taking screenshots, managing tabs, handling forms and auth, waiting for content, running multiple browser sessions in parallel, and troubleshooting common failures. Use when the user asks to interact with a website, fill a form, click something, extract data, take a screenshot, log into a site, test a web app, or automate any browser task.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1553,1554,1555],{"name":1524,"slug":1525,"type":15},{"name":1530,"slug":1531,"type":15},{"name":1556,"slug":1557,"type":15},"Navigation","navigation","2026-07-26T05:47:42.378419",{"slug":1560,"name":1560,"fn":1561,"description":1562,"org":1563,"tags":1564,"stars":1532,"repoUrl":1533,"updatedAt":1573},"derive-client","reverse engineer internal APIs from browser traffic","Reverse-engineer a website's internal API by recording browser traffic into a HAR file, then generate a standalone client or CLI that calls the endpoints directly, with no browser needed after the first recording. Use when asked to \"derive a client\", \"build a CLI for \u003Csite>\", \"reverse engineer this site's API\", \"record network requests\", \"turn this site into an API\", or when the same site will be automated repeatedly and direct HTTP calls would beat driving the browser every time.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1565,1568,1569,1570],{"name":1566,"slug":1567,"type":15},"API Development","api-development",{"name":1527,"slug":1528,"type":15},{"name":1530,"slug":1531,"type":15},{"name":1571,"slug":1572,"type":15},"Web Scraping","web-scraping","2026-07-20T06:24:11.928835",{"slug":1575,"name":1575,"fn":1576,"description":1577,"org":1578,"tags":1579,"stars":1532,"repoUrl":1533,"updatedAt":1590},"dogfood","perform exploratory testing on web applications","Systematically explore and test a web application to find bugs, UX issues, and other problems. Use when asked to \"dogfood\", \"QA\", \"exploratory test\", \"find issues\", \"bug hunt\", \"test this app\u002Fsite\u002Fplatform\", or review the quality of a web application. Produces a structured report with full reproduction evidence -- step-by-step screenshots, repro videos, and detailed repro steps for every issue -- so findings can be handed directly to the responsible teams.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1580,1581,1584,1587],{"name":1530,"slug":1531,"type":15},{"name":1582,"slug":1583,"type":15},"Debugging","debugging",{"name":1585,"slug":1586,"type":15},"QA","qa",{"name":1588,"slug":1589,"type":15},"Testing","testing","2026-07-17T06:07:41.421482",{"slug":1592,"name":1592,"fn":1593,"description":1594,"org":1595,"tags":1596,"stars":1532,"repoUrl":1533,"updatedAt":1602},"electron","automate Electron desktop applications","Automate Electron desktop apps (VS Code, Slack, Discord, Figma, Notion, Spotify, etc.) using agent-browser via Chrome DevTools Protocol. Use when the user needs to interact with an Electron app, automate a desktop app, connect to a running app, control a native app, or test an Electron application. Triggers include \"automate Slack app\", \"control VS Code\", \"interact with Discord app\", \"test this Electron app\", \"connect to desktop app\", or any task requiring automation of a native Electron application.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1597,1598,1599],{"name":1524,"slug":1525,"type":15},{"name":1530,"slug":1531,"type":15},{"name":1600,"slug":1601,"type":15},"Desktop","desktop","2026-07-17T06:08:28.007783",{"slug":1604,"name":1604,"fn":1605,"description":1606,"org":1607,"tags":1608,"stars":1532,"repoUrl":1533,"updatedAt":1615},"slack","interact with Slack workspaces","Interact with Slack workspaces using browser automation. Use when the user needs to check unread channels, navigate Slack, send messages, extract data, find information, search conversations, or automate any Slack task. Triggers include \"check my Slack\", \"what channels have unreads\", \"send a message to\", \"search Slack for\", \"extract from Slack\", \"find who said\", or any task requiring programmatic Slack interaction.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1609,1610,1613],{"name":1530,"slug":1531,"type":15},{"name":1611,"slug":1612,"type":15},"Messaging","messaging",{"name":1614,"slug":1604,"type":15},"Slack","2026-07-17T06:08:27.679015",{"slug":1617,"name":1617,"fn":1618,"description":1619,"org":1620,"tags":1621,"stars":1532,"repoUrl":1533,"updatedAt":1628},"vercel-sandbox","run browser automation in Vercel Sandbox","Run agent-browser + Chrome inside Vercel Sandbox microVMs for browser automation from any Vercel-deployed app. Use when the user needs browser automation in a Vercel app (Next.js, SvelteKit, Nuxt, Remix, Astro, etc.), wants to run headless Chrome without binary size limits, needs persistent browser sessions across commands, or wants ephemeral isolated browser environments. Triggers include \"Vercel Sandbox browser\", \"microVM Chrome\", \"agent-browser in sandbox\", \"browser automation on Vercel\", or any task requiring Chrome in a Vercel Sandbox.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1622,1623,1624,1625],{"name":1527,"slug":1528,"type":15},{"name":1530,"slug":1531,"type":15},{"name":1588,"slug":1589,"type":15},{"name":1626,"slug":1627,"type":15},"Vercel","vercel","2026-07-17T06:08:28.349899",{"slug":1630,"name":1630,"fn":1631,"description":1632,"org":1633,"tags":1634,"stars":1639,"repoUrl":1640,"updatedAt":1641},"deploy-to-vercel","deploy applications to Vercel","Deploy applications and websites to Vercel. Use when the user requests deployment actions like \"deploy my app\", \"deploy and give me the link\", \"push this live\", or \"create a preview deployment\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1635,1638],{"name":1636,"slug":1637,"type":15},"Deployment","deployment",{"name":1626,"slug":1627,"type":15},28993,"https:\u002F\u002Fgithub.com\u002Fvercel-labs\u002Fagent-skills","2026-07-17T06:08:41.18374",{"slug":1643,"name":1643,"fn":1644,"description":1645,"org":1646,"tags":1647,"stars":1639,"repoUrl":1640,"updatedAt":1653},"vercel-cli-with-tokens","manage Vercel projects via CLI","Deploy and manage projects on Vercel using token-based authentication. Use when working with Vercel CLI using access tokens rather than interactive login — e.g. \"deploy to vercel\", \"set up vercel\", \"add environment variables to vercel\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1648,1651,1652],{"name":1649,"slug":1650,"type":15},"CLI","cli",{"name":1636,"slug":1637,"type":15},{"name":1626,"slug":1627,"type":15},"2026-07-17T06:08:41.84179",{"slug":1655,"name":1655,"fn":1656,"description":1657,"org":1658,"tags":1659,"stars":1639,"repoUrl":1640,"updatedAt":1672},"vercel-composition-patterns","implement scalable React composition patterns","React composition patterns that scale. Use when refactoring components with boolean prop proliferation, building flexible component libraries, or designing reusable APIs. Triggers on tasks involving compound components, render props, context providers, or component architecture. Includes React 19 API changes.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1660,1663,1666,1669],{"name":1661,"slug":1662,"type":15},"Best Practices","best-practices",{"name":1664,"slug":1665,"type":15},"Frontend","frontend",{"name":1667,"slug":1668,"type":15},"React","react",{"name":1670,"slug":1671,"type":15},"UI Components","ui-components","2026-07-17T06:05:40.576913",{"slug":1674,"name":1674,"fn":1675,"description":1676,"org":1677,"tags":1678,"stars":1639,"repoUrl":1640,"updatedAt":1687},"vercel-optimize","optimize Vercel project performance and costs","Use for Vercel cost and performance optimization on deployed projects, especially Next.js, SvelteKit, Nuxt, and limited Astro apps. Collect Vercel metrics, usage, project config, and code scan results first; investigate only metric-backed candidates; produce ranked recommendations grounded in verified files and version-aware Vercel\u002Fframework docs. Trigger for Vercel bill reduction, slow or expensive routes, caching opportunities, Function Invocations, Build Minutes, Fast Data Transfer, Core Web Vitals, Bot Management, Fluid compute, or cost breakdown requests.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1679,1682,1683,1686],{"name":1680,"slug":1681,"type":15},"Cost Optimization","cost-optimization",{"name":1636,"slug":1637,"type":15},{"name":1684,"slug":1685,"type":15},"Performance","performance",{"name":1626,"slug":1627,"type":15},"2026-07-17T06:04:08.327515",100]