[{"data":1,"prerenderedAt":2348},["ShallowReactive",2],{"navigation_docs_en":3,"-en-auth-social-standalone":441,"-en-auth-social-standalone-surround":2343},[4,45,60,81,112,129,146,173,197,213,242,279,356,372,384,403,429],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":44},"Getting Started","i-lucide-rocket","\u002Fen\u002Fgetting-started","en\u002F1.getting-started",[10,15,20,24,29,34,39],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fen\u002Fgetting-started\u002Fintroduction","en\u002F1.getting-started\u002F1.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Coming from Laravel","\u002Fen\u002Fgetting-started\u002Fcoming-from-laravel","en\u002F1.getting-started\u002F2.coming-from-laravel","i-lucide-arrow-right-left",{"title":21,"path":22,"stem":23,"icon":19},"Coming from Symfony","\u002Fen\u002Fgetting-started\u002Fcoming-from-symfony","en\u002F1.getting-started\u002F3.coming-from-symfony",{"title":25,"path":26,"stem":27,"icon":28},"Installation","\u002Fen\u002Fgetting-started\u002Finstallation","en\u002F1.getting-started\u002F4.installation","i-lucide-download",{"title":30,"path":31,"stem":32,"icon":33},"Your first module","\u002Fen\u002Fgetting-started\u002Ffirst-module","en\u002F1.getting-started\u002F5.first-module","i-lucide-package-plus",{"title":35,"path":36,"stem":37,"icon":38},"Project structure","\u002Fen\u002Fgetting-started\u002Fproject-structure","en\u002F1.getting-started\u002F6.project-structure","i-lucide-folder-tree",{"title":40,"path":41,"stem":42,"icon":43},"The shop sample application","\u002Fen\u002Fgetting-started\u002Fsample-app","en\u002F1.getting-started\u002F7.sample-app","i-lucide-shopping-cart",false,{"title":46,"icon":47,"path":48,"stem":49,"children":50,"page":44},"queue","i-lucide-layers","\u002Fen\u002Fqueue","en\u002F10.queue",[51,56],{"title":52,"path":53,"stem":54,"icon":55},"Overview","\u002Fen\u002Fqueue\u002Foverview","en\u002F10.queue\u002F1.overview","i-lucide-list-ordered",{"title":57,"path":58,"stem":59,"icon":47},"Tasks","\u002Fen\u002Fqueue\u002Ftasks","en\u002F10.queue\u002F2.tasks",{"title":61,"icon":62,"path":63,"stem":64,"children":65,"page":44},"events","i-lucide-workflow","\u002Fen\u002Fevents","en\u002F11.events",[66,71,76],{"title":67,"path":68,"stem":69,"icon":70},"Events","\u002Fen\u002Fevents\u002Foverview","en\u002F11.events\u002F1.overview","i-lucide-radio",{"title":72,"path":73,"stem":74,"icon":75},"Outbox","\u002Fen\u002Fevents\u002Foutbox","en\u002F11.events\u002F2.outbox","i-lucide-inbox",{"title":77,"path":78,"stem":79,"icon":80},"Scheduling","\u002Fen\u002Fevents\u002Fscheduler","en\u002F11.events\u002F3.scheduler","i-lucide-calendar-clock",{"title":82,"icon":83,"path":84,"stem":85,"children":86,"page":44},"auth","i-lucide-shield-check","\u002Fen\u002Fauth","en\u002F12.auth",[87,92,97,102,107],{"title":88,"path":89,"stem":90,"icon":91},"Authentication","\u002Fen\u002Fauth\u002Foverview","en\u002F12.auth\u002F1.overview","i-lucide-key-round",{"title":93,"path":94,"stem":95,"icon":96},"RBAC","\u002Fen\u002Fauth\u002Frbac","en\u002F12.auth\u002F2.rbac","i-lucide-users",{"title":98,"path":99,"stem":100,"icon":101},"Policies","\u002Fen\u002Fauth\u002Fpolicy","en\u002F12.auth\u002F3.policy","i-lucide-gavel",{"title":103,"path":104,"stem":105,"icon":106},"Using auth standalone","\u002Fen\u002Fauth\u002Fstandalone","en\u002F12.auth\u002F4.standalone","i-lucide-plug",{"title":108,"path":109,"stem":110,"icon":111},"Social login with goth, standalone","\u002Fen\u002Fauth\u002Fsocial-standalone","en\u002F12.auth\u002F5.social-standalone","i-lucide-log-in",{"title":113,"icon":114,"path":115,"stem":116,"children":117,"page":44},"mail","i-lucide-mail","\u002Fen\u002Fmail","en\u002F13.mail",[118,121,125],{"title":52,"path":119,"stem":120,"icon":114},"\u002Fen\u002Fmail\u002Foverview","en\u002F13.mail\u002F1.overview",{"title":122,"path":123,"stem":124,"icon":114},"SMTP","\u002Fen\u002Fmail\u002Fsmtp","en\u002F13.mail\u002F2.smtp",{"title":126,"path":127,"stem":128,"icon":114},"MJML","\u002Fen\u002Fmail\u002Fmjml","en\u002F13.mail\u002F3.mjml",{"title":130,"icon":131,"path":132,"stem":133,"children":134,"page":44},"notify","i-lucide-bell","\u002Fen\u002Fnotify","en\u002F14.notify",[135,138,142],{"title":52,"path":136,"stem":137,"icon":131},"\u002Fen\u002Fnotify\u002Foverview","en\u002F14.notify\u002F1.overview",{"title":139,"path":140,"stem":141,"icon":131},"Database channel","\u002Fen\u002Fnotify\u002Fdatabase","en\u002F14.notify\u002F2.database",{"title":143,"path":144,"stem":145,"icon":131},"Broadcast channel","\u002Fen\u002Fnotify\u002Fbroadcast","en\u002F14.notify\u002F3.broadcast",{"title":147,"icon":148,"path":149,"stem":150,"children":151,"page":44},"storage","i-lucide-hard-drive","\u002Fen\u002Fstorage","en\u002F15.storage",[152,155,159,163,168],{"title":52,"path":153,"stem":154,"icon":148},"\u002Fen\u002Fstorage\u002Foverview","en\u002F15.storage\u002F1.overview",{"title":156,"path":157,"stem":158},"Disk API","\u002Fen\u002Fstorage\u002Fdisk-api","en\u002F15.storage\u002F2.disk-api",{"title":160,"path":161,"stem":162,"icon":148},"Memory and local disk","\u002Fen\u002Fstorage\u002Flocal","en\u002F15.storage\u002F3.local",{"title":164,"path":165,"stem":166,"icon":167},"S3","\u002Fen\u002Fstorage\u002Fs3","en\u002F15.storage\u002F4.s3","i-lucide-cloud",{"title":169,"path":170,"stem":171,"icon":172},"Testing","\u002Fen\u002Fstorage\u002Ftesting","en\u002F15.storage\u002F5.testing","i-lucide-flask-conical",{"title":174,"icon":175,"path":176,"stem":177,"children":178,"page":44},"telemetry","i-lucide-activity","\u002Fen\u002Ftelemetry","en\u002F16.telemetry",[179,182,187,192],{"title":52,"path":180,"stem":181,"icon":175},"\u002Fen\u002Ftelemetry\u002Foverview","en\u002F16.telemetry\u002F1.overview",{"title":183,"path":184,"stem":185,"icon":186},"Logging","\u002Fen\u002Ftelemetry\u002Flogging","en\u002F16.telemetry\u002F2.logging","i-lucide-scroll-text",{"title":188,"path":189,"stem":190,"icon":191},"OpenTelemetry","\u002Fen\u002Ftelemetry\u002Fopentelemetry","en\u002F16.telemetry\u002F3.opentelemetry","i-lucide-radar",{"title":193,"path":194,"stem":195,"icon":196},"Sentry","\u002Fen\u002Ftelemetry\u002Fsentry","en\u002F16.telemetry\u002F4.sentry","i-lucide-bug",{"title":198,"icon":199,"path":200,"stem":201,"children":202,"page":44},"Reference","i-lucide-book-open","\u002Fen\u002Freference","en\u002F17.reference",[203,208],{"title":204,"path":205,"stem":206,"icon":207},"Configuration","\u002Fen\u002Freference\u002Fconfiguration","en\u002F17.reference\u002F1.configuration","i-lucide-settings",{"title":209,"path":210,"stem":211,"icon":212},"External dependencies","\u002Fen\u002Freference\u002Fdependencies","en\u002F17.reference\u002F2.dependencies","i-lucide-package",{"title":214,"icon":215,"path":216,"stem":217,"children":218,"page":44},"Concepts","i-lucide-lightbulb","\u002Fen\u002Fconcepts","en\u002F2.concepts",[219,223,228,232,237],{"title":220,"path":221,"stem":222,"icon":47},"Architecture","\u002Fen\u002Fconcepts\u002Farchitecture","en\u002F2.concepts\u002F1.architecture",{"title":224,"path":225,"stem":226,"icon":227},"Error model","\u002Fen\u002Fconcepts\u002Ferror-model","en\u002F2.concepts\u002F2.error-model","i-lucide-shield-alert",{"title":204,"path":229,"stem":230,"icon":231},"\u002Fen\u002Fconcepts\u002Fconfiguration","en\u002F2.concepts\u002F3.configuration","i-lucide-settings-2",{"title":233,"path":234,"stem":235,"icon":236},"Codegen pipeline","\u002Fen\u002Fconcepts\u002Fcodegen-pipeline","en\u002F2.concepts\u002F4.codegen-pipeline","i-lucide-file-json",{"title":238,"path":239,"stem":240,"icon":241},"Design patterns","\u002Fen\u002Fconcepts\u002Fdesign-patterns","en\u002F2.concepts\u002F5.design-patterns","i-lucide-puzzle",{"title":243,"icon":244,"path":245,"stem":246,"children":247,"page":44},"Kit","i-lucide-box","\u002Fen\u002Fkit","en\u002F3.kit",[248,251,256,261,266,270,275],{"title":52,"path":249,"stem":250,"icon":244},"\u002Fen\u002Fkit\u002Foverview","en\u002F3.kit\u002F1.overview",{"title":252,"path":253,"stem":254,"icon":255},"Application lifecycle","\u002Fen\u002Fkit\u002Fapp","en\u002F3.kit\u002F2.app","i-lucide-power",{"title":257,"path":258,"stem":259,"icon":260},"Server","\u002Fen\u002Fkit\u002Fserver","en\u002F3.kit\u002F3.server","i-lucide-server",{"title":262,"path":263,"stem":264,"icon":265},"Worker","\u002Fen\u002Fkit\u002Fworker","en\u002F3.kit\u002F4.worker","i-lucide-cog",{"title":267,"path":268,"stem":269,"icon":70},"Realtime","\u002Fen\u002Fkit\u002Frealtime","en\u002F3.kit\u002F5.realtime",{"title":271,"path":272,"stem":273,"icon":274},"Migrations","\u002Fen\u002Fkit\u002Fmigrations","en\u002F3.kit\u002F6.migrations","i-lucide-file-stack",{"title":276,"path":277,"stem":278,"icon":111},"Social login","\u002Fen\u002Fkit\u002Fsocial-login","en\u002F3.kit\u002F7.social-login",{"title":280,"icon":281,"path":282,"stem":283,"children":284,"page":44},"CLI Reference","i-lucide-terminal","\u002Fen\u002Fcli","en\u002F4.cli",[285,288,293,298,303,308,313,318,323,328,333,337,342,347,351],{"title":52,"path":286,"stem":287,"icon":281},"\u002Fen\u002Fcli\u002Foverview","en\u002F4.cli\u002F1.overview",{"title":289,"path":290,"stem":291,"icon":292},"add db","\u002Fen\u002Fcli\u002Fadd-db","en\u002F4.cli\u002F10.add-db","i-lucide-database-zap",{"title":294,"path":295,"stem":296,"icon":297},"add compose \u002F add docker","\u002Fen\u002Fcli\u002Fadd-compose","en\u002F4.cli\u002F11.add-compose","i-lucide-container",{"title":299,"path":300,"stem":301,"icon":302},"add mail","\u002Fen\u002Fcli\u002Fadd-mail","en\u002F4.cli\u002F12.add-mail","i-lucide-mail-plus",{"title":304,"path":305,"stem":306,"icon":307},"add notification","\u002Fen\u002Fcli\u002Fadd-notification","en\u002F4.cli\u002F13.add-notification","i-lucide-bell-plus",{"title":309,"path":310,"stem":311,"icon":312},"add realtime","\u002Fen\u002Fcli\u002Fadd-realtime","en\u002F4.cli\u002F14.add-realtime","i-lucide-radio-tower",{"title":314,"path":315,"stem":316,"icon":317},"add seeder","\u002Fen\u002Fcli\u002Fadd-seeder","en\u002F4.cli\u002F15.add-seeder","i-lucide-sprout",{"title":319,"path":320,"stem":321,"icon":322},"new project","\u002Fen\u002Fcli\u002Fnew-project","en\u002F4.cli\u002F2.new-project","i-lucide-folder-plus",{"title":324,"path":325,"stem":326,"icon":327},"new module","\u002Fen\u002Fcli\u002Fnew-module","en\u002F4.cli\u002F3.new-module","i-lucide-blocks",{"title":329,"path":330,"stem":331,"icon":332},"add surface","\u002Fen\u002Fcli\u002Fadd-surface","en\u002F4.cli\u002F4.add-surface","i-lucide-layers-2",{"title":334,"path":335,"stem":336,"icon":244},"add core","\u002Fen\u002Fcli\u002Fadd-core","en\u002F4.cli\u002F5.add-core",{"title":338,"path":339,"stem":340,"icon":341},"add handler","\u002Fen\u002Fcli\u002Fadd-handler","en\u002F4.cli\u002F6.add-handler","i-lucide-webhook",{"title":343,"path":344,"stem":345,"icon":346},"new migration","\u002Fen\u002Fcli\u002Fnew-migration","en\u002F4.cli\u002F7.new-migration","i-lucide-file-plus",{"title":348,"path":349,"stem":350,"icon":265},"worker generators","\u002Fen\u002Fcli\u002Fworker-generators","en\u002F4.cli\u002F8.worker-generators",{"title":352,"path":353,"stem":354,"icon":355},"upgrade templates","\u002Fen\u002Fcli\u002Fupgrade-templates","en\u002F4.cli\u002F9.upgrade-templates","i-lucide-refresh-cw",{"title":357,"icon":227,"path":358,"stem":359,"children":360,"page":44},"errs","\u002Fen\u002Ferrs","en\u002F5.errs",[361,364,368],{"title":52,"path":362,"stem":363,"icon":227},"\u002Fen\u002Ferrs\u002Foverview","en\u002F5.errs\u002F1.overview",{"title":365,"path":366,"stem":367},"API","\u002Fen\u002Ferrs\u002Fapi","en\u002F5.errs\u002F2.api",{"title":369,"path":370,"stem":371,"icon":227},"Integration","\u002Fen\u002Ferrs\u002Fintegration","en\u002F5.errs\u002F3.integration",{"title":373,"icon":231,"path":374,"stem":375,"children":376,"page":44},"envconf","\u002Fen\u002Fenvconf","en\u002F6.envconf",[377,380],{"title":52,"path":378,"stem":379,"icon":231},"\u002Fen\u002Fenvconf\u002Foverview","en\u002F6.envconf\u002F1.overview",{"title":381,"path":382,"stem":383},"Recipes","\u002Fen\u002Fenvconf\u002Frecipes","en\u002F6.envconf\u002F2.recipes",{"title":385,"icon":386,"path":387,"stem":388,"children":389,"page":44},"httperr","i-lucide-globe","\u002Fen\u002Fhttperr","en\u002F7.httperr",[390,393,398],{"title":52,"path":391,"stem":392,"icon":386},"\u002Fen\u002Fhttperr\u002Foverview","en\u002F7.httperr\u002F1.overview",{"title":394,"path":395,"stem":396,"icon":397},"Error responses","\u002Fen\u002Fhttperr\u002Ferror-responses","en\u002F7.httperr\u002F2.error-responses","i-lucide-octagon-alert",{"title":399,"path":400,"stem":401,"icon":402},"Validation","\u002Fen\u002Fhttperr\u002Fvalidation","en\u002F7.httperr\u002F3.validation","i-lucide-badge-check",{"title":404,"icon":405,"path":406,"stem":407,"children":408,"page":44},"dbx","i-lucide-database","\u002Fen\u002Fdbx","en\u002F8.dbx",[409,412,416,420,425],{"title":52,"path":410,"stem":411,"icon":405},"\u002Fen\u002Fdbx\u002Foverview","en\u002F8.dbx\u002F1.overview",{"title":413,"path":414,"stem":415,"icon":106},"pgx","\u002Fen\u002Fdbx\u002Fpg","en\u002F8.dbx\u002F2.pg",{"title":417,"path":418,"stem":419,"icon":47},"bun","\u002Fen\u002Fdbx\u002Fbunx","en\u002F8.dbx\u002F3.bunx",{"title":421,"path":422,"stem":423,"icon":424},"Transactions","\u002Fen\u002Fdbx\u002Ftransactions","en\u002F8.dbx\u002F4.transactions","i-lucide-git-merge",{"title":426,"path":427,"stem":428,"icon":317},"Seeders","\u002Fen\u002Fdbx\u002Fseed","en\u002F8.dbx\u002F5.seed",{"title":430,"icon":55,"path":431,"stem":432,"children":433,"page":44},"paginate","\u002Fen\u002Fpaginate","en\u002F9.paginate",[434,437],{"title":52,"path":435,"stem":436,"icon":55},"\u002Fen\u002Fpaginate\u002Foverview","en\u002F9.paginate\u002F1.overview",{"title":438,"path":439,"stem":440,"icon":19},"Cursor pagination","\u002Fen\u002Fpaginate\u002Fcursor","en\u002F9.paginate\u002F2.cursor",{"id":442,"title":108,"body":443,"description":2336,"extension":2337,"links":2338,"meta":2339,"navigation":2340,"path":109,"seo":2341,"stem":110,"__hash__":2342},"docs_en\u002Fen\u002F12.auth\u002F5.social-standalone.md",{"type":444,"value":445,"toc":2329},"minimark",[446,479,521,570,575,601,605,1013,1020,1024,1045,1285,1297,1301,1335,2276,2294,2298,2325],[447,448,449,450,454,455,459,460,463,464,470,471,478],"p",{},"The ",[451,452,453],"a",{"href":277},"social login"," page covers ",[456,457,458],"code",{},"--social","-generated code inside a gpsystem project. This page is the standalone version of the same idea: a plain ",[456,461,462],{},"net\u002Fhttp"," app, no gpsystem CLI, no oapi-codegen strict-server layer, that uses ",[451,465,469],{"href":466,"rel":467},"https:\u002F\u002Fgithub.com\u002Fmarkbates\u002Fgoth",[468],"nofollow","markbates\u002Fgoth"," to talk to Discord, Facebook and Google, and ",[451,472,475],{"href":473,"rel":474},"https:\u002F\u002Fgithub.com\u002Fgp-system\u002Fauth",[468],[456,476,477],{},"gp-system\u002Fauth"," to turn the result into the same JWT\u002Frefresh pair an email+password login would produce.",[480,481,482,485,486,490,491,493,494,496,497,500,501,504,505,507,508,510,511,514,515,517,518,520],"note",{},[456,483,484],{},"goth"," is ",[487,488,489],"strong",{},"your project's"," dependency here, not ",[456,492,82],{},"'s: ",[456,495,82],{},"'s own ",[456,498,499],{},"go.mod"," requires only ",[456,502,503],{},"golang-jwt\u002Fjwt\u002Fv5"," (see ",[451,506,103],{"href":104},"). Nothing in this example changes that; ",[456,509,484],{}," and ",[456,512,513],{},"gorilla\u002Fsessions"," land in your ",[456,516,499],{},", not ",[456,519,82],{},"'s.",[522,523,528],"pre",{"className":524,"code":525,"language":526,"meta":527,"style":527},"language-sh shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","go mod init example.com\u002Fsocial-demo\ngo get github.com\u002Fgp-system\u002Fauth\ngo get github.com\u002Fmarkbates\u002Fgoth\n","sh","",[456,529,530,549,560],{"__ignoreMap":527},[531,532,535,539,543,546],"span",{"class":533,"line":534},"line",1,[531,536,538],{"class":537},"sBMFI","go",[531,540,542],{"class":541},"sfazB"," mod",[531,544,545],{"class":541}," init",[531,547,548],{"class":541}," example.com\u002Fsocial-demo\n",[531,550,552,554,557],{"class":533,"line":551},2,[531,553,538],{"class":537},[531,555,556],{"class":541}," get",[531,558,559],{"class":541}," github.com\u002Fgp-system\u002Fauth\n",[531,561,563,565,567],{"class":533,"line":562},3,[531,564,538],{"class":537},[531,566,556],{"class":541},[531,568,569],{"class":541}," github.com\u002Fmarkbates\u002Fgoth\n",[571,572,574],"h2",{"id":573},"why-this-is-simpler-than-the-generated-version","Why this is simpler than the generated version",[447,576,577,578,581,582,585,586,589,590,593,594,596,597,600],{},"The kit's generated ",[456,579,580],{},"social"," package signs its own ",[456,583,584],{},"state"," parameter instead of using a server-side session, because the oapi-codegen strict-server handler signature (",[456,587,588],{},"func(ctx, req) (resp, error)",") never exposes the raw ",[456,591,592],{},"http.ResponseWriter",", so there is nowhere to set a cookie between the begin and callback requests. A hand-rolled ",[456,595,462],{}," app has no such restriction: it can use goth's own ",[456,598,599],{},"gothic"," helper package as-is, cookie session and all, which is the shape goth's own docs assume.",[571,602,604],{"id":603},"registering-providers","Registering providers",[522,606,609],{"className":607,"code":608,"language":538,"meta":527,"style":527},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","package main\n\nimport (\n    \"os\"\n\n    \"github.com\u002Fmarkbates\u002Fgoth\"\n    \"github.com\u002Fmarkbates\u002Fgoth\u002Fproviders\u002Fdiscord\"\n    \"github.com\u002Fmarkbates\u002Fgoth\u002Fproviders\u002Ffacebook\"\n    \"github.com\u002Fmarkbates\u002Fgoth\u002Fproviders\u002Fgoogle\"\n)\n\nconst baseURL = \"http:\u002F\u002Flocalhost:8080\"\n\nfunc registerProviders() {\n    goth.UseProviders(\n        discord.New(os.Getenv(\"DISCORD_CLIENT_ID\"), os.Getenv(\"DISCORD_CLIENT_SECRET\"),\n            baseURL+\"\u002Fauth\u002Fdiscord\u002Fcallback\", discord.ScopeIdentify, discord.ScopeEmail),\n        facebook.New(os.Getenv(\"FACEBOOK_CLIENT_ID\"), os.Getenv(\"FACEBOOK_CLIENT_SECRET\"),\n            baseURL+\"\u002Fauth\u002Ffacebook\u002Fcallback\", \"email\"),\n        google.New(os.Getenv(\"GOOGLE_CLIENT_ID\"), os.Getenv(\"GOOGLE_CLIENT_SECRET\"),\n            baseURL+\"\u002Fauth\u002Fgoogle\u002Fcallback\", \"email\", \"profile\"),\n    )\n}\n",[456,610,611,620,626,635,647,652,662,672,682,692,698,703,724,729,745,760,813,851,897,922,968,1001,1007],{"__ignoreMap":527},[531,612,613,617],{"class":533,"line":534},[531,614,616],{"class":615},"sMK4o","package",[531,618,619],{"class":537}," main\n",[531,621,622],{"class":533,"line":551},[531,623,625],{"emptyLinePlaceholder":624},true,"\n",[531,627,628,632],{"class":533,"line":562},[531,629,631],{"class":630},"s7zQu","import",[531,633,634],{"class":615}," (\n",[531,636,638,641,644],{"class":533,"line":637},4,[531,639,640],{"class":615},"    \"",[531,642,643],{"class":537},"os",[531,645,646],{"class":615},"\"\n",[531,648,650],{"class":533,"line":649},5,[531,651,625],{"emptyLinePlaceholder":624},[531,653,655,657,660],{"class":533,"line":654},6,[531,656,640],{"class":615},[531,658,659],{"class":537},"github.com\u002Fmarkbates\u002Fgoth",[531,661,646],{"class":615},[531,663,665,667,670],{"class":533,"line":664},7,[531,666,640],{"class":615},[531,668,669],{"class":537},"github.com\u002Fmarkbates\u002Fgoth\u002Fproviders\u002Fdiscord",[531,671,646],{"class":615},[531,673,675,677,680],{"class":533,"line":674},8,[531,676,640],{"class":615},[531,678,679],{"class":537},"github.com\u002Fmarkbates\u002Fgoth\u002Fproviders\u002Ffacebook",[531,681,646],{"class":615},[531,683,685,687,690],{"class":533,"line":684},9,[531,686,640],{"class":615},[531,688,689],{"class":537},"github.com\u002Fmarkbates\u002Fgoth\u002Fproviders\u002Fgoogle",[531,691,646],{"class":615},[531,693,695],{"class":533,"line":694},10,[531,696,697],{"class":615},")\n",[531,699,701],{"class":533,"line":700},11,[531,702,625],{"emptyLinePlaceholder":624},[531,704,706,709,713,716,719,722],{"class":533,"line":705},12,[531,707,708],{"class":615},"const",[531,710,712],{"class":711},"sTEyZ"," baseURL ",[531,714,715],{"class":615},"=",[531,717,718],{"class":615}," \"",[531,720,721],{"class":541},"http:\u002F\u002Flocalhost:8080",[531,723,646],{"class":615},[531,725,727],{"class":533,"line":726},13,[531,728,625],{"emptyLinePlaceholder":624},[531,730,732,735,739,742],{"class":533,"line":731},14,[531,733,734],{"class":615},"func",[531,736,738],{"class":737},"s2Zo4"," registerProviders",[531,740,741],{"class":615},"()",[531,743,744],{"class":615}," {\n",[531,746,748,751,754,757],{"class":533,"line":747},15,[531,749,750],{"class":711},"    goth",[531,752,753],{"class":615},".",[531,755,756],{"class":737},"UseProviders",[531,758,759],{"class":615},"(\n",[531,761,763,766,768,771,774,776,778,781,783,786,789,791,794,797,799,801,803,805,808,810],{"class":533,"line":762},16,[531,764,765],{"class":711},"        discord",[531,767,753],{"class":615},[531,769,770],{"class":737},"New",[531,772,773],{"class":615},"(",[531,775,643],{"class":711},[531,777,753],{"class":615},[531,779,780],{"class":737},"Getenv",[531,782,773],{"class":615},[531,784,785],{"class":615},"\"",[531,787,788],{"class":541},"DISCORD_CLIENT_ID",[531,790,785],{"class":615},[531,792,793],{"class":615},"),",[531,795,796],{"class":711}," os",[531,798,753],{"class":615},[531,800,780],{"class":737},[531,802,773],{"class":615},[531,804,785],{"class":615},[531,806,807],{"class":541},"DISCORD_CLIENT_SECRET",[531,809,785],{"class":615},[531,811,812],{"class":615},"),\n",[531,814,816,819,822,824,827,829,832,835,837,840,842,844,846,849],{"class":533,"line":815},17,[531,817,818],{"class":711},"            baseURL",[531,820,821],{"class":615},"+",[531,823,785],{"class":615},[531,825,826],{"class":541},"\u002Fauth\u002Fdiscord\u002Fcallback",[531,828,785],{"class":615},[531,830,831],{"class":615},",",[531,833,834],{"class":711}," discord",[531,836,753],{"class":615},[531,838,839],{"class":711},"ScopeIdentify",[531,841,831],{"class":615},[531,843,834],{"class":711},[531,845,753],{"class":615},[531,847,848],{"class":711},"ScopeEmail",[531,850,812],{"class":615},[531,852,854,857,859,861,863,865,867,869,871,873,876,878,880,882,884,886,888,890,893,895],{"class":533,"line":853},18,[531,855,856],{"class":711},"        facebook",[531,858,753],{"class":615},[531,860,770],{"class":737},[531,862,773],{"class":615},[531,864,643],{"class":711},[531,866,753],{"class":615},[531,868,780],{"class":737},[531,870,773],{"class":615},[531,872,785],{"class":615},[531,874,875],{"class":541},"FACEBOOK_CLIENT_ID",[531,877,785],{"class":615},[531,879,793],{"class":615},[531,881,796],{"class":711},[531,883,753],{"class":615},[531,885,780],{"class":737},[531,887,773],{"class":615},[531,889,785],{"class":615},[531,891,892],{"class":541},"FACEBOOK_CLIENT_SECRET",[531,894,785],{"class":615},[531,896,812],{"class":615},[531,898,900,902,904,906,909,911,913,915,918,920],{"class":533,"line":899},19,[531,901,818],{"class":711},[531,903,821],{"class":615},[531,905,785],{"class":615},[531,907,908],{"class":541},"\u002Fauth\u002Ffacebook\u002Fcallback",[531,910,785],{"class":615},[531,912,831],{"class":615},[531,914,718],{"class":615},[531,916,917],{"class":541},"email",[531,919,785],{"class":615},[531,921,812],{"class":615},[531,923,925,928,930,932,934,936,938,940,942,944,947,949,951,953,955,957,959,961,964,966],{"class":533,"line":924},20,[531,926,927],{"class":711},"        google",[531,929,753],{"class":615},[531,931,770],{"class":737},[531,933,773],{"class":615},[531,935,643],{"class":711},[531,937,753],{"class":615},[531,939,780],{"class":737},[531,941,773],{"class":615},[531,943,785],{"class":615},[531,945,946],{"class":541},"GOOGLE_CLIENT_ID",[531,948,785],{"class":615},[531,950,793],{"class":615},[531,952,796],{"class":711},[531,954,753],{"class":615},[531,956,780],{"class":737},[531,958,773],{"class":615},[531,960,785],{"class":615},[531,962,963],{"class":541},"GOOGLE_CLIENT_SECRET",[531,965,785],{"class":615},[531,967,812],{"class":615},[531,969,971,973,975,977,980,982,984,986,988,990,992,994,997,999],{"class":533,"line":970},21,[531,972,818],{"class":711},[531,974,821],{"class":615},[531,976,785],{"class":615},[531,978,979],{"class":541},"\u002Fauth\u002Fgoogle\u002Fcallback",[531,981,785],{"class":615},[531,983,831],{"class":615},[531,985,718],{"class":615},[531,987,917],{"class":541},[531,989,785],{"class":615},[531,991,831],{"class":615},[531,993,718],{"class":615},[531,995,996],{"class":541},"profile",[531,998,785],{"class":615},[531,1000,812],{"class":615},[531,1002,1004],{"class":533,"line":1003},22,[531,1005,1006],{"class":615},"    )\n",[531,1008,1010],{"class":533,"line":1009},23,[531,1011,1012],{"class":615},"}\n",[447,1014,1015,1016,753],{},"Each provider's app console (Discord Developer Portal, Meta for Developers, Google Cloud Console) needs the matching callback URL registered, exactly as in the ",[451,1017,1019],{"href":1018},"\u002Fen\u002Fkit\u002Fsocial-login#provider-side-setup-and-env-vars","generated version's provider-side setup table",[571,1021,1023],{"id":1022},"routing-with-gothic","Routing with gothic",[447,1025,1026,510,1029,1032,1033,1036,1037,1040,1041,1044],{},[456,1027,1028],{},"gothic.BeginAuthHandler",[456,1030,1031],{},"gothic.CompleteUserAuth"," read the provider name straight off the request; with Go's ",[456,1034,1035],{},"net\u002Fhttp.ServeMux"," route patterns (Go 1.22+), ",[456,1038,1039],{},"req.PathValue(\"provider\")"," is one of the places ",[456,1042,1043],{},"gothic.GetProviderName"," already checks, so a plain stdlib mux is enough, no router library required:",[522,1046,1048],{"className":607,"code":1047,"language":538,"meta":527,"style":527},"package main\n\nimport (\n    \"net\u002Fhttp\"\n    \"os\"\n\n    \"github.com\u002Fgorilla\u002Fsessions\"\n    \"github.com\u002Fmarkbates\u002Fgoth\u002Fgothic\"\n)\n\nfunc main() {\n    gothic.Store = sessions.NewCookieStore([]byte(os.Getenv(\"SESSION_SECRET\")))\n    registerProviders()\n\n    mux := http.NewServeMux()\n    mux.HandleFunc(\"GET \u002Fauth\u002F{provider}\", gothic.BeginAuthHandler)\n    mux.HandleFunc(\"GET \u002Fauth\u002F{provider}\u002Fcallback\", handleCallback)\n    http.ListenAndServe(\":8080\", mux)\n}\n",[456,1049,1050,1056,1060,1066,1074,1082,1086,1095,1104,1108,1112,1123,1170,1178,1182,1200,1231,1255,1281],{"__ignoreMap":527},[531,1051,1052,1054],{"class":533,"line":534},[531,1053,616],{"class":615},[531,1055,619],{"class":537},[531,1057,1058],{"class":533,"line":551},[531,1059,625],{"emptyLinePlaceholder":624},[531,1061,1062,1064],{"class":533,"line":562},[531,1063,631],{"class":630},[531,1065,634],{"class":615},[531,1067,1068,1070,1072],{"class":533,"line":637},[531,1069,640],{"class":615},[531,1071,462],{"class":537},[531,1073,646],{"class":615},[531,1075,1076,1078,1080],{"class":533,"line":649},[531,1077,640],{"class":615},[531,1079,643],{"class":537},[531,1081,646],{"class":615},[531,1083,1084],{"class":533,"line":654},[531,1085,625],{"emptyLinePlaceholder":624},[531,1087,1088,1090,1093],{"class":533,"line":664},[531,1089,640],{"class":615},[531,1091,1092],{"class":537},"github.com\u002Fgorilla\u002Fsessions",[531,1094,646],{"class":615},[531,1096,1097,1099,1102],{"class":533,"line":674},[531,1098,640],{"class":615},[531,1100,1101],{"class":537},"github.com\u002Fmarkbates\u002Fgoth\u002Fgothic",[531,1103,646],{"class":615},[531,1105,1106],{"class":533,"line":684},[531,1107,697],{"class":615},[531,1109,1110],{"class":533,"line":694},[531,1111,625],{"emptyLinePlaceholder":624},[531,1113,1114,1116,1119,1121],{"class":533,"line":700},[531,1115,734],{"class":615},[531,1117,1118],{"class":737}," main",[531,1120,741],{"class":615},[531,1122,744],{"class":615},[531,1124,1125,1128,1130,1133,1135,1138,1140,1143,1146,1150,1152,1154,1156,1158,1160,1162,1165,1167],{"class":533,"line":705},[531,1126,1127],{"class":711},"    gothic",[531,1129,753],{"class":615},[531,1131,1132],{"class":711},"Store ",[531,1134,715],{"class":615},[531,1136,1137],{"class":711}," sessions",[531,1139,753],{"class":615},[531,1141,1142],{"class":737},"NewCookieStore",[531,1144,1145],{"class":615},"([]",[531,1147,1149],{"class":1148},"spNyl","byte",[531,1151,773],{"class":615},[531,1153,643],{"class":711},[531,1155,753],{"class":615},[531,1157,780],{"class":737},[531,1159,773],{"class":615},[531,1161,785],{"class":615},[531,1163,1164],{"class":541},"SESSION_SECRET",[531,1166,785],{"class":615},[531,1168,1169],{"class":615},")))\n",[531,1171,1172,1175],{"class":533,"line":726},[531,1173,1174],{"class":737},"    registerProviders",[531,1176,1177],{"class":615},"()\n",[531,1179,1180],{"class":533,"line":731},[531,1181,625],{"emptyLinePlaceholder":624},[531,1183,1184,1187,1190,1193,1195,1198],{"class":533,"line":747},[531,1185,1186],{"class":711},"    mux ",[531,1188,1189],{"class":615},":=",[531,1191,1192],{"class":711}," http",[531,1194,753],{"class":615},[531,1196,1197],{"class":737},"NewServeMux",[531,1199,1177],{"class":615},[531,1201,1202,1205,1207,1210,1212,1214,1217,1219,1221,1224,1226,1229],{"class":533,"line":762},[531,1203,1204],{"class":711},"    mux",[531,1206,753],{"class":615},[531,1208,1209],{"class":737},"HandleFunc",[531,1211,773],{"class":615},[531,1213,785],{"class":615},[531,1215,1216],{"class":541},"GET \u002Fauth\u002F{provider}",[531,1218,785],{"class":615},[531,1220,831],{"class":615},[531,1222,1223],{"class":711}," gothic",[531,1225,753],{"class":615},[531,1227,1228],{"class":711},"BeginAuthHandler",[531,1230,697],{"class":615},[531,1232,1233,1235,1237,1239,1241,1243,1246,1248,1250,1253],{"class":533,"line":815},[531,1234,1204],{"class":711},[531,1236,753],{"class":615},[531,1238,1209],{"class":737},[531,1240,773],{"class":615},[531,1242,785],{"class":615},[531,1244,1245],{"class":541},"GET \u002Fauth\u002F{provider}\u002Fcallback",[531,1247,785],{"class":615},[531,1249,831],{"class":615},[531,1251,1252],{"class":711}," handleCallback",[531,1254,697],{"class":615},[531,1256,1257,1260,1262,1265,1267,1269,1272,1274,1276,1279],{"class":533,"line":853},[531,1258,1259],{"class":711},"    http",[531,1261,753],{"class":615},[531,1263,1264],{"class":737},"ListenAndServe",[531,1266,773],{"class":615},[531,1268,785],{"class":615},[531,1270,1271],{"class":541},":8080",[531,1273,785],{"class":615},[531,1275,831],{"class":615},[531,1277,1278],{"class":711}," mux",[531,1280,697],{"class":615},[531,1282,1283],{"class":533,"line":899},[531,1284,1012],{"class":615},[447,1286,1287,1289,1290,1292,1293,1296],{},[456,1288,1164],{}," signs the cookie ",[456,1291,599],{}," stores the OAuth session in between the begin and callback requests; treat it the same as ",[456,1294,1295],{},"JWT_SECRET",", a per-environment value, never checked in.",[571,1298,1300],{"id":1299},"the-callback-gothuser-to-a-gp-systemauth-token","The callback: goth.User to a gp-system\u002Fauth token",[447,1302,1303,1304,1306,1307,1309,1310,1313,1314,1317,1318,1317,1321,1317,1324,1317,1327,1330,1331,1334],{},"This is the part that touches ",[456,1305,82],{},". ",[456,1308,1031],{}," hands back a ",[456,1311,1312],{},"goth.User"," (its ",[456,1315,1316],{},"Provider",", ",[456,1319,1320],{},"UserID",[456,1322,1323],{},"Email",[456,1325,1326],{},"Name",[456,1328,1329],{},"AvatarURL"," fields); ",[456,1332,1333],{},"findOrCreateUser"," below is where you'd apply the same lookup rule the generated version documents (match on provider + provider key first, fall back to email, reject linking onto an unverified local account), just written by hand instead of generated:",[522,1336,1338],{"className":607,"code":1337,"language":538,"meta":527,"style":527},"package main\n\nimport (\n    \"encoding\u002Fjson\"\n    \"net\u002Fhttp\"\n    \"time\"\n\n    \"github.com\u002Fgp-system\u002Fauth\"\n    \"github.com\u002Fmarkbates\u002Fgoth\"\n    \"github.com\u002Fmarkbates\u002Fgoth\u002Fgothic\"\n)\n\nvar issuer = auth.NewTokenIssuer[auth.DefaultClaims](auth.Config{\n    Secret:    \"dev-secret-change-me\", \u002F\u002F in real code: from env\u002Fsecret manager\n    AccessTTL: 15 * time.Minute,\n    Issuer:    \"social-demo\",\n})\n\nfunc handleCallback(w http.ResponseWriter, r *http.Request) {\n    gothUser, err := gothic.CompleteUserAuth(w, r)\n    if err != nil {\n        http.Error(w, \"social login failed\", http.StatusUnauthorized)\n        return\n    }\n\n    user, err := findOrCreateUser(gothUser)\n    if err != nil {\n        http.Error(w, \"internal error\", http.StatusInternalServerError)\n        return\n    }\n\n    claims := auth.NewDefaultClaims(auth.Config{Issuer: \"social-demo\", AccessTTL: 15 * time.Minute},\n        user.ID, user.Username, user.Roles, user.Permissions)\n    token, err := issuer.Sign(claims)\n    if err != nil {\n        http.Error(w, \"internal error\", http.StatusInternalServerError)\n        return\n    }\n\n    refresh, err := auth.NewRefreshToken()\n    if err != nil {\n        http.Error(w, \"internal error\", http.StatusInternalServerError)\n        return\n    }\n    \u002F\u002F store a hash of refresh against user.ID here, same as an email+password login would\n\n    w.Header().Set(\"Content-Type\", \"application\u002Fjson\")\n    json.NewEncoder(w).Encode(map[string]string{\"accessToken\": token, \"refreshToken\": refresh})\n}\n\n\u002F\u002F findOrCreateUser is your own persistence, not part of auth or goth. Look\n\u002F\u002F up by (provider, gothUser.UserID) first (returning user); then by email\n\u002F\u002F (link to an existing, verified account, or reject if that email belongs\n\u002F\u002F to an unverified one); otherwise create a new, pre-verified user, since\n\u002F\u002F the provider already vouched for the email address.\nfunc findOrCreateUser(gothUser goth.User) (localUser, error) {\n    panic(\"left as an exercise: your own users table\")\n}\n\ntype localUser struct {\n    ID          string\n    Username    string\n    Roles       []string\n    Permissions []string\n}\n",[456,1339,1340,1346,1350,1356,1365,1373,1382,1386,1395,1403,1411,1415,1419,1460,1481,1506,1522,1527,1531,1570,1599,1614,1648,1653,1659,1664,1686,1699,1732,1737,1742,1747,1804,1845,1872,1885,1916,1921,1926,1931,1952,1965,1996,2001,2006,2012,2017,2054,2116,2121,2126,2132,2138,2144,2150,2156,2192,2209,2214,2219,2233,2242,2250,2261,2271],{"__ignoreMap":527},[531,1341,1342,1344],{"class":533,"line":534},[531,1343,616],{"class":615},[531,1345,619],{"class":537},[531,1347,1348],{"class":533,"line":551},[531,1349,625],{"emptyLinePlaceholder":624},[531,1351,1352,1354],{"class":533,"line":562},[531,1353,631],{"class":630},[531,1355,634],{"class":615},[531,1357,1358,1360,1363],{"class":533,"line":637},[531,1359,640],{"class":615},[531,1361,1362],{"class":537},"encoding\u002Fjson",[531,1364,646],{"class":615},[531,1366,1367,1369,1371],{"class":533,"line":649},[531,1368,640],{"class":615},[531,1370,462],{"class":537},[531,1372,646],{"class":615},[531,1374,1375,1377,1380],{"class":533,"line":654},[531,1376,640],{"class":615},[531,1378,1379],{"class":537},"time",[531,1381,646],{"class":615},[531,1383,1384],{"class":533,"line":664},[531,1385,625],{"emptyLinePlaceholder":624},[531,1387,1388,1390,1393],{"class":533,"line":674},[531,1389,640],{"class":615},[531,1391,1392],{"class":537},"github.com\u002Fgp-system\u002Fauth",[531,1394,646],{"class":615},[531,1396,1397,1399,1401],{"class":533,"line":684},[531,1398,640],{"class":615},[531,1400,659],{"class":537},[531,1402,646],{"class":615},[531,1404,1405,1407,1409],{"class":533,"line":694},[531,1406,640],{"class":615},[531,1408,1101],{"class":537},[531,1410,646],{"class":615},[531,1412,1413],{"class":533,"line":700},[531,1414,697],{"class":615},[531,1416,1417],{"class":533,"line":705},[531,1418,625],{"emptyLinePlaceholder":624},[531,1420,1421,1424,1427,1429,1432,1434,1437,1440,1442,1444,1447,1450,1452,1454,1457],{"class":533,"line":726},[531,1422,1423],{"class":615},"var",[531,1425,1426],{"class":711}," issuer ",[531,1428,715],{"class":615},[531,1430,1431],{"class":711}," auth",[531,1433,753],{"class":615},[531,1435,1436],{"class":737},"NewTokenIssuer",[531,1438,1439],{"class":615},"[",[531,1441,82],{"class":537},[531,1443,753],{"class":615},[531,1445,1446],{"class":537},"DefaultClaims",[531,1448,1449],{"class":615},"](",[531,1451,82],{"class":537},[531,1453,753],{"class":615},[531,1455,1456],{"class":537},"Config",[531,1458,1459],{"class":615},"{\n",[531,1461,1462,1465,1468,1470,1473,1475,1477],{"class":533,"line":731},[531,1463,1464],{"class":711},"    Secret",[531,1466,1467],{"class":615},":",[531,1469,640],{"class":615},[531,1471,1472],{"class":541},"dev-secret-change-me",[531,1474,785],{"class":615},[531,1476,831],{"class":615},[531,1478,1480],{"class":1479},"sHwdD"," \u002F\u002F in real code: from env\u002Fsecret manager\n",[531,1482,1483,1486,1488,1492,1495,1498,1500,1503],{"class":533,"line":747},[531,1484,1485],{"class":711},"    AccessTTL",[531,1487,1467],{"class":615},[531,1489,1491],{"class":1490},"sbssI"," 15",[531,1493,1494],{"class":615}," *",[531,1496,1497],{"class":711}," time",[531,1499,753],{"class":615},[531,1501,1502],{"class":711},"Minute",[531,1504,1505],{"class":615},",\n",[531,1507,1508,1511,1513,1515,1518,1520],{"class":533,"line":762},[531,1509,1510],{"class":711},"    Issuer",[531,1512,1467],{"class":615},[531,1514,640],{"class":615},[531,1516,1517],{"class":541},"social-demo",[531,1519,785],{"class":615},[531,1521,1505],{"class":615},[531,1523,1524],{"class":533,"line":815},[531,1525,1526],{"class":615},"})\n",[531,1528,1529],{"class":533,"line":853},[531,1530,625],{"emptyLinePlaceholder":624},[531,1532,1533,1535,1537,1539,1543,1545,1547,1550,1552,1555,1557,1560,1562,1565,1568],{"class":533,"line":899},[531,1534,734],{"class":615},[531,1536,1252],{"class":737},[531,1538,773],{"class":615},[531,1540,1542],{"class":1541},"sHdIc","w",[531,1544,1192],{"class":537},[531,1546,753],{"class":615},[531,1548,1549],{"class":537},"ResponseWriter",[531,1551,831],{"class":615},[531,1553,1554],{"class":1541}," r",[531,1556,1494],{"class":615},[531,1558,1559],{"class":537},"http",[531,1561,753],{"class":615},[531,1563,1564],{"class":537},"Request",[531,1566,1567],{"class":615},")",[531,1569,744],{"class":615},[531,1571,1572,1575,1577,1580,1582,1584,1586,1589,1591,1593,1595,1597],{"class":533,"line":924},[531,1573,1574],{"class":711},"    gothUser",[531,1576,831],{"class":615},[531,1578,1579],{"class":711}," err ",[531,1581,1189],{"class":615},[531,1583,1223],{"class":711},[531,1585,753],{"class":615},[531,1587,1588],{"class":737},"CompleteUserAuth",[531,1590,773],{"class":615},[531,1592,1542],{"class":711},[531,1594,831],{"class":615},[531,1596,1554],{"class":711},[531,1598,697],{"class":615},[531,1600,1601,1604,1606,1609,1612],{"class":533,"line":970},[531,1602,1603],{"class":630},"    if",[531,1605,1579],{"class":711},[531,1607,1608],{"class":615},"!=",[531,1610,1611],{"class":615}," nil",[531,1613,744],{"class":615},[531,1615,1616,1619,1621,1624,1626,1628,1630,1632,1635,1637,1639,1641,1643,1646],{"class":533,"line":1003},[531,1617,1618],{"class":711},"        http",[531,1620,753],{"class":615},[531,1622,1623],{"class":737},"Error",[531,1625,773],{"class":615},[531,1627,1542],{"class":711},[531,1629,831],{"class":615},[531,1631,718],{"class":615},[531,1633,1634],{"class":541},"social login failed",[531,1636,785],{"class":615},[531,1638,831],{"class":615},[531,1640,1192],{"class":711},[531,1642,753],{"class":615},[531,1644,1645],{"class":711},"StatusUnauthorized",[531,1647,697],{"class":615},[531,1649,1650],{"class":533,"line":1009},[531,1651,1652],{"class":630},"        return\n",[531,1654,1656],{"class":533,"line":1655},24,[531,1657,1658],{"class":615},"    }\n",[531,1660,1662],{"class":533,"line":1661},25,[531,1663,625],{"emptyLinePlaceholder":624},[531,1665,1667,1670,1672,1674,1676,1679,1681,1684],{"class":533,"line":1666},26,[531,1668,1669],{"class":711},"    user",[531,1671,831],{"class":615},[531,1673,1579],{"class":711},[531,1675,1189],{"class":615},[531,1677,1678],{"class":737}," findOrCreateUser",[531,1680,773],{"class":615},[531,1682,1683],{"class":711},"gothUser",[531,1685,697],{"class":615},[531,1687,1689,1691,1693,1695,1697],{"class":533,"line":1688},27,[531,1690,1603],{"class":630},[531,1692,1579],{"class":711},[531,1694,1608],{"class":615},[531,1696,1611],{"class":615},[531,1698,744],{"class":615},[531,1700,1702,1704,1706,1708,1710,1712,1714,1716,1719,1721,1723,1725,1727,1730],{"class":533,"line":1701},28,[531,1703,1618],{"class":711},[531,1705,753],{"class":615},[531,1707,1623],{"class":737},[531,1709,773],{"class":615},[531,1711,1542],{"class":711},[531,1713,831],{"class":615},[531,1715,718],{"class":615},[531,1717,1718],{"class":541},"internal error",[531,1720,785],{"class":615},[531,1722,831],{"class":615},[531,1724,1192],{"class":711},[531,1726,753],{"class":615},[531,1728,1729],{"class":711},"StatusInternalServerError",[531,1731,697],{"class":615},[531,1733,1735],{"class":533,"line":1734},29,[531,1736,1652],{"class":630},[531,1738,1740],{"class":533,"line":1739},30,[531,1741,1658],{"class":615},[531,1743,1745],{"class":533,"line":1744},31,[531,1746,625],{"emptyLinePlaceholder":624},[531,1748,1750,1753,1755,1757,1759,1762,1764,1766,1768,1770,1773,1776,1778,1780,1782,1784,1786,1789,1791,1793,1795,1797,1799,1801],{"class":533,"line":1749},32,[531,1751,1752],{"class":711},"    claims ",[531,1754,1189],{"class":615},[531,1756,1431],{"class":711},[531,1758,753],{"class":615},[531,1760,1761],{"class":737},"NewDefaultClaims",[531,1763,773],{"class":615},[531,1765,82],{"class":537},[531,1767,753],{"class":615},[531,1769,1456],{"class":537},[531,1771,1772],{"class":615},"{",[531,1774,1775],{"class":711},"Issuer",[531,1777,1467],{"class":615},[531,1779,718],{"class":615},[531,1781,1517],{"class":541},[531,1783,785],{"class":615},[531,1785,831],{"class":615},[531,1787,1788],{"class":711}," AccessTTL",[531,1790,1467],{"class":615},[531,1792,1491],{"class":1490},[531,1794,1494],{"class":615},[531,1796,1497],{"class":711},[531,1798,753],{"class":615},[531,1800,1502],{"class":711},[531,1802,1803],{"class":615},"},\n",[531,1805,1807,1810,1812,1815,1817,1820,1822,1825,1827,1829,1831,1834,1836,1838,1840,1843],{"class":533,"line":1806},33,[531,1808,1809],{"class":711},"        user",[531,1811,753],{"class":615},[531,1813,1814],{"class":711},"ID",[531,1816,831],{"class":615},[531,1818,1819],{"class":711}," user",[531,1821,753],{"class":615},[531,1823,1824],{"class":711},"Username",[531,1826,831],{"class":615},[531,1828,1819],{"class":711},[531,1830,753],{"class":615},[531,1832,1833],{"class":711},"Roles",[531,1835,831],{"class":615},[531,1837,1819],{"class":711},[531,1839,753],{"class":615},[531,1841,1842],{"class":711},"Permissions",[531,1844,697],{"class":615},[531,1846,1848,1851,1853,1855,1857,1860,1862,1865,1867,1870],{"class":533,"line":1847},34,[531,1849,1850],{"class":711},"    token",[531,1852,831],{"class":615},[531,1854,1579],{"class":711},[531,1856,1189],{"class":615},[531,1858,1859],{"class":711}," issuer",[531,1861,753],{"class":615},[531,1863,1864],{"class":737},"Sign",[531,1866,773],{"class":615},[531,1868,1869],{"class":711},"claims",[531,1871,697],{"class":615},[531,1873,1875,1877,1879,1881,1883],{"class":533,"line":1874},35,[531,1876,1603],{"class":630},[531,1878,1579],{"class":711},[531,1880,1608],{"class":615},[531,1882,1611],{"class":615},[531,1884,744],{"class":615},[531,1886,1888,1890,1892,1894,1896,1898,1900,1902,1904,1906,1908,1910,1912,1914],{"class":533,"line":1887},36,[531,1889,1618],{"class":711},[531,1891,753],{"class":615},[531,1893,1623],{"class":737},[531,1895,773],{"class":615},[531,1897,1542],{"class":711},[531,1899,831],{"class":615},[531,1901,718],{"class":615},[531,1903,1718],{"class":541},[531,1905,785],{"class":615},[531,1907,831],{"class":615},[531,1909,1192],{"class":711},[531,1911,753],{"class":615},[531,1913,1729],{"class":711},[531,1915,697],{"class":615},[531,1917,1919],{"class":533,"line":1918},37,[531,1920,1652],{"class":630},[531,1922,1924],{"class":533,"line":1923},38,[531,1925,1658],{"class":615},[531,1927,1929],{"class":533,"line":1928},39,[531,1930,625],{"emptyLinePlaceholder":624},[531,1932,1934,1937,1939,1941,1943,1945,1947,1950],{"class":533,"line":1933},40,[531,1935,1936],{"class":711},"    refresh",[531,1938,831],{"class":615},[531,1940,1579],{"class":711},[531,1942,1189],{"class":615},[531,1944,1431],{"class":711},[531,1946,753],{"class":615},[531,1948,1949],{"class":737},"NewRefreshToken",[531,1951,1177],{"class":615},[531,1953,1955,1957,1959,1961,1963],{"class":533,"line":1954},41,[531,1956,1603],{"class":630},[531,1958,1579],{"class":711},[531,1960,1608],{"class":615},[531,1962,1611],{"class":615},[531,1964,744],{"class":615},[531,1966,1968,1970,1972,1974,1976,1978,1980,1982,1984,1986,1988,1990,1992,1994],{"class":533,"line":1967},42,[531,1969,1618],{"class":711},[531,1971,753],{"class":615},[531,1973,1623],{"class":737},[531,1975,773],{"class":615},[531,1977,1542],{"class":711},[531,1979,831],{"class":615},[531,1981,718],{"class":615},[531,1983,1718],{"class":541},[531,1985,785],{"class":615},[531,1987,831],{"class":615},[531,1989,1192],{"class":711},[531,1991,753],{"class":615},[531,1993,1729],{"class":711},[531,1995,697],{"class":615},[531,1997,1999],{"class":533,"line":1998},43,[531,2000,1652],{"class":630},[531,2002,2004],{"class":533,"line":2003},44,[531,2005,1658],{"class":615},[531,2007,2009],{"class":533,"line":2008},45,[531,2010,2011],{"class":1479},"    \u002F\u002F store a hash of refresh against user.ID here, same as an email+password login would\n",[531,2013,2015],{"class":533,"line":2014},46,[531,2016,625],{"emptyLinePlaceholder":624},[531,2018,2020,2023,2025,2028,2031,2034,2036,2038,2041,2043,2045,2047,2050,2052],{"class":533,"line":2019},47,[531,2021,2022],{"class":711},"    w",[531,2024,753],{"class":615},[531,2026,2027],{"class":737},"Header",[531,2029,2030],{"class":615},"().",[531,2032,2033],{"class":737},"Set",[531,2035,773],{"class":615},[531,2037,785],{"class":615},[531,2039,2040],{"class":541},"Content-Type",[531,2042,785],{"class":615},[531,2044,831],{"class":615},[531,2046,718],{"class":615},[531,2048,2049],{"class":541},"application\u002Fjson",[531,2051,785],{"class":615},[531,2053,697],{"class":615},[531,2055,2057,2060,2062,2065,2067,2069,2072,2075,2078,2081,2084,2086,2088,2090,2093,2095,2097,2100,2102,2104,2107,2109,2111,2114],{"class":533,"line":2056},48,[531,2058,2059],{"class":711},"    json",[531,2061,753],{"class":615},[531,2063,2064],{"class":737},"NewEncoder",[531,2066,773],{"class":615},[531,2068,1542],{"class":711},[531,2070,2071],{"class":615},").",[531,2073,2074],{"class":737},"Encode",[531,2076,2077],{"class":615},"(map[",[531,2079,2080],{"class":1148},"string",[531,2082,2083],{"class":615},"]",[531,2085,2080],{"class":1148},[531,2087,1772],{"class":615},[531,2089,785],{"class":615},[531,2091,2092],{"class":541},"accessToken",[531,2094,785],{"class":615},[531,2096,1467],{"class":615},[531,2098,2099],{"class":711}," token",[531,2101,831],{"class":615},[531,2103,718],{"class":615},[531,2105,2106],{"class":541},"refreshToken",[531,2108,785],{"class":615},[531,2110,1467],{"class":615},[531,2112,2113],{"class":711}," refresh",[531,2115,1526],{"class":615},[531,2117,2119],{"class":533,"line":2118},49,[531,2120,1012],{"class":615},[531,2122,2124],{"class":533,"line":2123},50,[531,2125,625],{"emptyLinePlaceholder":624},[531,2127,2129],{"class":533,"line":2128},51,[531,2130,2131],{"class":1479},"\u002F\u002F findOrCreateUser is your own persistence, not part of auth or goth. Look\n",[531,2133,2135],{"class":533,"line":2134},52,[531,2136,2137],{"class":1479},"\u002F\u002F up by (provider, gothUser.UserID) first (returning user); then by email\n",[531,2139,2141],{"class":533,"line":2140},53,[531,2142,2143],{"class":1479},"\u002F\u002F (link to an existing, verified account, or reject if that email belongs\n",[531,2145,2147],{"class":533,"line":2146},54,[531,2148,2149],{"class":1479},"\u002F\u002F to an unverified one); otherwise create a new, pre-verified user, since\n",[531,2151,2153],{"class":533,"line":2152},55,[531,2154,2155],{"class":1479},"\u002F\u002F the provider already vouched for the email address.\n",[531,2157,2159,2161,2163,2165,2167,2170,2172,2175,2177,2180,2183,2185,2188,2190],{"class":533,"line":2158},56,[531,2160,734],{"class":615},[531,2162,1678],{"class":737},[531,2164,773],{"class":615},[531,2166,1683],{"class":1541},[531,2168,2169],{"class":537}," goth",[531,2171,753],{"class":615},[531,2173,2174],{"class":537},"User",[531,2176,1567],{"class":615},[531,2178,2179],{"class":615}," (",[531,2181,2182],{"class":537},"localUser",[531,2184,831],{"class":615},[531,2186,2187],{"class":1148}," error",[531,2189,1567],{"class":615},[531,2191,744],{"class":615},[531,2193,2195,2198,2200,2202,2205,2207],{"class":533,"line":2194},57,[531,2196,2197],{"class":737},"    panic",[531,2199,773],{"class":615},[531,2201,785],{"class":615},[531,2203,2204],{"class":541},"left as an exercise: your own users table",[531,2206,785],{"class":615},[531,2208,697],{"class":615},[531,2210,2212],{"class":533,"line":2211},58,[531,2213,1012],{"class":615},[531,2215,2217],{"class":533,"line":2216},59,[531,2218,625],{"emptyLinePlaceholder":624},[531,2220,2222,2225,2228,2231],{"class":533,"line":2221},60,[531,2223,2224],{"class":615},"type",[531,2226,2227],{"class":537}," localUser",[531,2229,2230],{"class":615}," struct",[531,2232,744],{"class":615},[531,2234,2236,2239],{"class":533,"line":2235},61,[531,2237,2238],{"class":711},"    ID          ",[531,2240,2241],{"class":1148},"string\n",[531,2243,2245,2248],{"class":533,"line":2244},62,[531,2246,2247],{"class":711},"    Username    ",[531,2249,2241],{"class":1148},[531,2251,2253,2256,2259],{"class":533,"line":2252},63,[531,2254,2255],{"class":711},"    Roles       ",[531,2257,2258],{"class":615},"[]",[531,2260,2241],{"class":1148},[531,2262,2264,2267,2269],{"class":533,"line":2263},64,[531,2265,2266],{"class":711},"    Permissions ",[531,2268,2258],{"class":615},[531,2270,2241],{"class":1148},[531,2272,2274],{"class":533,"line":2273},65,[531,2275,1012],{"class":615},[447,2277,2278,2279,2282,2283,2286,2287,2290,2291,2293],{},"From here on, every other page in this section applies unchanged: the token ",[456,2280,2281],{},"handleCallback"," returns is verified with the same ",[456,2284,2285],{},"auth.Identify","\u002F",[456,2288,2289],{},"rbac.CheckRole"," calls as a token minted from an email+password login, because it is one, just signed after a ",[456,2292,1312],{}," lookup instead of a bcrypt comparison.",[571,2295,2297],{"id":2296},"what-the-kit-adds-on-top","What the kit adds on top",[447,2299,2300,2301,2303,2304,2307,2308,2310,2311,2286,2314,2317,2318,2321,2322,2324],{},"The generated ",[456,2302,580],{}," package (",[456,2305,2306],{},"go tool gpsystem add auth --social discord,facebook,google",") is this same idea with three things bolted on that a real project usually wants: a signed, stateless ",[456,2309,584],{}," parameter instead of a cookie session (so it works from the strict-server handler shape), the account-linking rule implemented once against the kit's ",[456,2312,2313],{},"users",[456,2315,2316],{},"auth_credentials"," schema instead of a ",[456,2319,2320],{},"panic(\"left as an exercise\")",", and Apple's form_post callback and JWT client-secret handling. See ",[451,2323,276],{"href":277}," for that version.",[2326,2327,2328],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}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);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":527,"searchDepth":551,"depth":551,"links":2330},[2331,2332,2333,2334,2335],{"id":573,"depth":551,"text":574},{"id":603,"depth":551,"text":604},{"id":1022,"depth":551,"text":1023},{"id":1299,"depth":551,"text":1300},{"id":2296,"depth":551,"text":2297},"Wiring markbates\u002Fgoth (Discord, Facebook, Google) into a plain net\u002Fhttp app that only depends on gp-system\u002Fauth for tokens, no gpsystem tooling involved.","md",null,{},{"icon":111},{"title":108,"description":2336},"ISQ7wOz4ANVAwSWh-aEfkBjLSpDKsyEHv62jSE-K68A",[2344,2346],{"title":103,"path":104,"stem":105,"description":2345,"icon":106,"children":-1},"A complete, runnable example with no gp-system dependency beyond auth itself: HTTP and non-HTTP.",{"title":52,"path":119,"stem":120,"description":2347,"icon":114,"children":-1},"Email sending: fluent Message builder, MJML templates, a memory mailer for tests.",1785445894471]