[{"data":1,"prerenderedAt":792},["ShallowReactive",2],{"navigation":3,"\u002Fauth\u002Foauth-and-social-login":189,"\u002Fauth\u002Foauth-and-social-login-surround":787},[4,34,60,116,137,153],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":33},"Getting Started","i-lucide-rocket","\u002Fgetting-started","1.getting-started",[10,15,18,23,28],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fgetting-started\u002Fintroduction","1.getting-started\u002F1.introduction","i-lucide-house",{"title":5,"path":16,"stem":17,"icon":6},"\u002Fgetting-started\u002Fgetting-started","1.getting-started\u002F2.getting-started",{"title":19,"path":20,"stem":21,"icon":22},"Choosing a Preset","\u002Fgetting-started\u002Fchoosing-a-preset","1.getting-started\u002F3.choosing-a-preset","i-lucide-git-branch",{"title":24,"path":25,"stem":26,"icon":27},"Deployment","\u002Fgetting-started\u002Fdeployment","1.getting-started\u002F4.deployment","i-lucide-cloud",{"title":29,"path":30,"stem":31,"icon":32},"Background Maintenance","\u002Fgetting-started\u002Fbackground-maintenance","1.getting-started\u002F5.background-maintenance","i-lucide-timer-reset",false,{"title":35,"icon":36,"path":37,"stem":38,"children":39,"page":33},"Build","i-lucide-wrench","\u002Fbuild","2.build",[40,45,50,55],{"title":41,"path":42,"stem":43,"icon":44},"Routers & Prefixes","\u002Fbuild\u002Frouters-and-prefixes","2.build\u002F1.routers-and-prefixes","i-lucide-route",{"title":46,"path":47,"stem":48,"icon":49},"Configuration","\u002Fbuild\u002Fconfiguration","2.build\u002F2.configuration","i-lucide-settings",{"title":51,"path":52,"stem":53,"icon":54},"Authorization Dependencies","\u002Fbuild\u002Fauthorization-dependencies","2.build\u002F3.authorization-dependencies","i-lucide-shield-check",{"title":56,"path":57,"stem":58,"icon":59},"Command Line","\u002Fbuild\u002Fcli","2.build\u002F4.cli","i-lucide-terminal",{"title":61,"icon":62,"path":63,"stem":64,"children":65,"page":33},"Auth","i-lucide-lock","\u002Fauth","3.auth",[66,71,76,81,86,91,96,101,106,111],{"title":67,"path":68,"stem":69,"icon":70},"OAuth & Social Login","\u002Fauth\u002Foauth-and-social-login","3.auth\u002F1.oauth-and-social-login","i-lucide-log-in",{"title":72,"path":73,"stem":74,"icon":75},"Multi-Frontend Support","\u002Fauth\u002Fmulti-frontend","3.auth\u002F10.multi-frontend","i-lucide-layout-grid",{"title":77,"path":78,"stem":79,"icon":80},"Sessions & Audit","\u002Fauth\u002Fsessions-and-audit","3.auth\u002F2.sessions-and-audit","i-lucide-monitor-smartphone",{"title":82,"path":83,"stem":84,"icon":85},"Passwordless & Messaging","\u002Fauth\u002Fpasswordless-and-messaging","3.auth\u002F3.passwordless-and-messaging","i-lucide-mail",{"title":87,"path":88,"stem":89,"icon":90},"JWT Tokens","\u002Fauth\u002Fjwt-tokens","3.auth\u002F4.jwt-tokens","i-lucide-key-round",{"title":92,"path":93,"stem":94,"icon":95},"User Management API","\u002Fauth\u002Fuser-management-api","3.auth\u002F5.user-management-api","i-lucide-users",{"title":97,"path":98,"stem":99,"icon":100},"User Invitations","\u002Fauth\u002Fuser-invitations","3.auth\u002F6.user-invitations","i-lucide-send",{"title":102,"path":103,"stem":104,"icon":105},"Roles & Permissions","\u002Fauth\u002Froles-and-permissions","3.auth\u002F7.roles-and-permissions","i-lucide-shield",{"title":107,"path":108,"stem":109,"icon":110},"ABAC","\u002Fauth\u002Fabac","3.auth\u002F8.abac","i-lucide-filter",{"title":112,"path":113,"stem":114,"icon":115},"User Status","\u002Fauth\u002Fuser-status","3.auth\u002F9.user-status","i-lucide-user-cog",{"title":117,"icon":118,"path":119,"stem":120,"children":121,"page":33},"Enterprise","i-lucide-building-2","\u002Fenterprise","4.enterprise",[122,127,132],{"title":123,"path":124,"stem":125,"icon":126},"Core Authorization Concepts","\u002Fenterprise\u002Fcore-authorization-concepts","4.enterprise\u002F1.core-authorization-concepts","i-lucide-network",{"title":128,"path":129,"stem":130,"icon":131},"Entities","\u002Fenterprise\u002Fentities","4.enterprise\u002F2.entities","i-lucide-folder-tree",{"title":133,"path":134,"stem":135,"icon":136},"Entity Memberships","\u002Fenterprise\u002Fentity-memberships","4.enterprise\u002F3.entity-memberships","i-lucide-user-plus",{"title":138,"icon":139,"path":140,"stem":141,"children":142,"page":33},"Integrations","i-lucide-plug","\u002Fintegrations","5.integrations",[143,148],{"title":144,"path":145,"stem":146,"icon":147},"API Keys","\u002Fintegrations\u002Fapi-keys","5.integrations\u002F1.api-keys","i-lucide-key",{"title":149,"path":150,"stem":151,"icon":152},"OutlabsAuth UI","\u002Fintegrations\u002Foutlabsauth-ui","5.integrations\u002F2.outlabsauth-ui","i-lucide-layout-dashboard",{"title":154,"icon":155,"path":156,"stem":157,"children":158,"page":33},"Reference","i-lucide-book-marked","\u002Freference","6.reference",[159,164,169,174,179,184],{"title":160,"path":161,"stem":162,"icon":163},"Data Models","\u002Freference\u002Fdata-models","6.reference\u002F1.data-models","i-lucide-database",{"title":165,"path":166,"stem":167,"icon":168},"Activity Tracking","\u002Freference\u002Factivity-tracking","6.reference\u002F2.activity-tracking","i-lucide-activity",{"title":170,"path":171,"stem":172,"icon":173},"Testing","\u002Freference\u002Ftesting","6.reference\u002F3.testing","i-lucide-flask-conical",{"title":175,"path":176,"stem":177,"icon":178},"Observability","\u002Freference\u002Fobservability","6.reference\u002F4.observability","i-lucide-eye",{"title":180,"path":181,"stem":182,"icon":183},"Metrics Reference","\u002Freference\u002Fmetrics-reference","6.reference\u002F5.metrics-reference","i-lucide-chart-bar",{"title":185,"path":186,"stem":187,"icon":188},"Log Events Reference","\u002Freference\u002Flog-events-reference","6.reference\u002F6.log-events-reference","i-lucide-scroll-text",{"id":190,"title":67,"body":191,"description":780,"extension":781,"links":782,"meta":783,"navigation":784,"path":68,"seo":785,"stem":69,"__hash__":786},"docs\u002F3.auth\u002F1.oauth-and-social-login.md",{"type":192,"value":193,"toc":769},"minimark",[194,198,213,218,251,255,263,482,493,496,501,529,533,593,597,608,655,662,666,672,701,704,708,738,742,765],[195,196,197],"p",{},"Add Google (or another provider) so users can sign in with an existing account.\nEmail\u002Fpassword keeps working without any of this — OAuth is opt-in by mounting\nrouters, not a constructor flag.",[195,199,200,201,205,206,212],{},"Runnable wiring: ",[202,203,204],"code",{},"examples\u002Fenterprise_rbac\u002Fmain.py"," (Google invite-only +\nassociate). Deeper design notes for maintainers:\n",[207,208,209],"a",{"href":83},[202,210,211],{},"docs\u002FAUTH_EXTENSIONS.md",".",[214,215,217],"h2",{"id":216},"how-it-fits","How it fits",[219,220,221,229,248],"ul",{},[222,223,224,225,228],"li",{},"Local ",[202,226,227],{},"User"," rows stay the account of record",[222,230,231,232,235,236,239,240,243,244,247],{},"Each linked provider is a ",[202,233,234],{},"SocialAccount"," row (",[202,237,238],{},"user_id",", ",[202,241,242],{},"provider",",\n",[202,245,246],{},"provider_user_id",", …)",[222,249,250],{},"Users can link several providers; unlinking is a row delete",[214,252,254],{"id":253},"enable-and-mount","Enable and mount",[195,256,257,258,262],{},"OAuth is ",[259,260,261],"strong",{},"not"," a constructor flag. You mount a router per provider:",[264,265,270],"pre",{"className":266,"code":267,"language":268,"meta":269,"style":269},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","from outlabs_auth.oauth.providers import get_google_client  # or GitHub \u002F Facebook helpers\nfrom outlabs_auth.routers import get_oauth_router, get_oauth_associate_router\n\ngoogle = get_google_client(\n    client_id=os.environ[\"GOOGLE_CLIENT_ID\"],\n    client_secret=os.environ[\"GOOGLE_CLIENT_SECRET\"],\n)\n\napp.include_router(\n    get_oauth_router(\n        google,\n        auth,\n        state_secret=os.environ[\"SECRET_KEY\"],  # or a dedicated OAuth state secret\n        prefix=\"\u002Fv1\u002Foauth\u002Fgoogle\",\n        redirect_url=\"https:\u002F\u002Fapi.example.com\u002Fv1\u002Foauth\u002Fgoogle\u002Fcallback\",\n        success_redirect_url=\"https:\u002F\u002Fapp.example.com\u002Fauth\u002Foauth\u002Fcallback\",\n        error_redirect_url=\"https:\u002F\u002Fapp.example.com\u002Fauth\u002Flogin\",\n        associate_by_email=True,\n        is_verified_by_default=True,\n        require_existing_user=True,  # invite-only: reject unknown emails\n        cookie_secure=True,\n    )\n)\n\n# Optional: link an extra provider while already signed in\napp.include_router(\n    get_oauth_associate_router(\n        google,\n        auth,\n        state_secret=os.environ[\"SECRET_KEY\"],\n        prefix=\"\u002Fv1\u002Foauth-associate\u002Fgoogle\",\n        redirect_url=\"https:\u002F\u002Fapi.example.com\u002Fv1\u002Foauth-associate\u002Fgoogle\u002Fcallback\",\n        success_redirect_url=\"https:\u002F\u002Fapp.example.com\u002Fapp\u002Faccount\",\n        cookie_secure=True,\n    )\n)\n","python","",[202,271,272,280,286,293,299,305,311,317,322,328,334,340,346,352,358,364,370,376,382,388,394,400,406,411,416,422,427,433,438,443,449,455,461,467,472,477],{"__ignoreMap":269},[273,274,277],"span",{"class":275,"line":276},"line",1,[273,278,279],{},"from outlabs_auth.oauth.providers import get_google_client  # or GitHub \u002F Facebook helpers\n",[273,281,283],{"class":275,"line":282},2,[273,284,285],{},"from outlabs_auth.routers import get_oauth_router, get_oauth_associate_router\n",[273,287,289],{"class":275,"line":288},3,[273,290,292],{"emptyLinePlaceholder":291},true,"\n",[273,294,296],{"class":275,"line":295},4,[273,297,298],{},"google = get_google_client(\n",[273,300,302],{"class":275,"line":301},5,[273,303,304],{},"    client_id=os.environ[\"GOOGLE_CLIENT_ID\"],\n",[273,306,308],{"class":275,"line":307},6,[273,309,310],{},"    client_secret=os.environ[\"GOOGLE_CLIENT_SECRET\"],\n",[273,312,314],{"class":275,"line":313},7,[273,315,316],{},")\n",[273,318,320],{"class":275,"line":319},8,[273,321,292],{"emptyLinePlaceholder":291},[273,323,325],{"class":275,"line":324},9,[273,326,327],{},"app.include_router(\n",[273,329,331],{"class":275,"line":330},10,[273,332,333],{},"    get_oauth_router(\n",[273,335,337],{"class":275,"line":336},11,[273,338,339],{},"        google,\n",[273,341,343],{"class":275,"line":342},12,[273,344,345],{},"        auth,\n",[273,347,349],{"class":275,"line":348},13,[273,350,351],{},"        state_secret=os.environ[\"SECRET_KEY\"],  # or a dedicated OAuth state secret\n",[273,353,355],{"class":275,"line":354},14,[273,356,357],{},"        prefix=\"\u002Fv1\u002Foauth\u002Fgoogle\",\n",[273,359,361],{"class":275,"line":360},15,[273,362,363],{},"        redirect_url=\"https:\u002F\u002Fapi.example.com\u002Fv1\u002Foauth\u002Fgoogle\u002Fcallback\",\n",[273,365,367],{"class":275,"line":366},16,[273,368,369],{},"        success_redirect_url=\"https:\u002F\u002Fapp.example.com\u002Fauth\u002Foauth\u002Fcallback\",\n",[273,371,373],{"class":275,"line":372},17,[273,374,375],{},"        error_redirect_url=\"https:\u002F\u002Fapp.example.com\u002Fauth\u002Flogin\",\n",[273,377,379],{"class":275,"line":378},18,[273,380,381],{},"        associate_by_email=True,\n",[273,383,385],{"class":275,"line":384},19,[273,386,387],{},"        is_verified_by_default=True,\n",[273,389,391],{"class":275,"line":390},20,[273,392,393],{},"        require_existing_user=True,  # invite-only: reject unknown emails\n",[273,395,397],{"class":275,"line":396},21,[273,398,399],{},"        cookie_secure=True,\n",[273,401,403],{"class":275,"line":402},22,[273,404,405],{},"    )\n",[273,407,409],{"class":275,"line":408},23,[273,410,316],{},[273,412,414],{"class":275,"line":413},24,[273,415,292],{"emptyLinePlaceholder":291},[273,417,419],{"class":275,"line":418},25,[273,420,421],{},"# Optional: link an extra provider while already signed in\n",[273,423,425],{"class":275,"line":424},26,[273,426,327],{},[273,428,430],{"class":275,"line":429},27,[273,431,432],{},"    get_oauth_associate_router(\n",[273,434,436],{"class":275,"line":435},28,[273,437,339],{},[273,439,441],{"class":275,"line":440},29,[273,442,345],{},[273,444,446],{"class":275,"line":445},30,[273,447,448],{},"        state_secret=os.environ[\"SECRET_KEY\"],\n",[273,450,452],{"class":275,"line":451},31,[273,453,454],{},"        prefix=\"\u002Fv1\u002Foauth-associate\u002Fgoogle\",\n",[273,456,458],{"class":275,"line":457},32,[273,459,460],{},"        redirect_url=\"https:\u002F\u002Fapi.example.com\u002Fv1\u002Foauth-associate\u002Fgoogle\u002Fcallback\",\n",[273,462,464],{"class":275,"line":463},33,[273,465,466],{},"        success_redirect_url=\"https:\u002F\u002Fapp.example.com\u002Fapp\u002Faccount\",\n",[273,468,470],{"class":275,"line":469},34,[273,471,399],{},[273,473,475],{"class":275,"line":474},35,[273,476,405],{},[273,478,480],{"class":275,"line":479},36,[273,481,316],{},[195,483,484,485,488,489,492],{},"Both factories are exported from ",[202,486,487],{},"outlabs_auth.routers"," since ",[202,490,491],{},"0.1.0a25","\n(the module paths above still work).",[195,494,495],{},"Register the callback URLs with the provider console exactly as mounted.",[497,498,500],"h3",{"id":499},"multi-frontend-oauth","Multi-frontend OAuth",[195,502,503,504,507,508,511,512,515,516,519,520,515,523,526,527,212],{},"When your mount serves several frontends, ",[202,505,506],{},"\u002Fauthorize"," accepts a registered\nfrontend profile key (",[202,509,510],{},"?app=portal","). The signed + persisted state binds that\nprofile and a per-flow nonce — concurrent same-provider flows from different\nfrontends coexist — and the callback lands on the bound profile's registered\n",[202,513,514],{},"oauth_success"," \u002F ",[202,517,518],{},"oauth_error"," routes. Construction-time\n",[202,521,522],{},"success_redirect_url",[202,524,525],{},"error_redirect_url"," remain the single-profile\ndegenerate case. See ",[207,528,72],{"href":73},[214,530,532],{"id":531},"important-flags","Important flags",[534,535,536,549],"table",{},[537,538,539],"thead",{},[540,541,542,546],"tr",{},[543,544,545],"th",{},"Flag",[543,547,548],{},"Meaning",[550,551,552,563,573,583],"tbody",{},[540,553,554,560],{},[555,556,557],"td",{},[202,558,559],{},"require_existing_user=True",[555,561,562],{},"Invite-only OAuth; unknown emails are rejected",[540,564,565,570],{},[555,566,567],{},[202,568,569],{},"associate_by_email=True",[555,571,572],{},"Link provider identity to an existing local user with the same email — only for providers whose email verification you trust",[540,574,575,580],{},[555,576,577],{},[202,578,579],{},"is_verified_by_default",[555,581,582],{},"Whether to treat the provider email as verified at link time",[540,584,585,590],{},[555,586,587],{},[202,588,589],{},"state_secret",[555,591,592],{},"Required; signs OAuth state",[214,594,596],{"id":595},"self-service-social-accounts","Self-service social accounts",[195,598,599,600,603,604,607],{},"With ",[202,601,602],{},"get_users_router"," mounted (e.g. ",[202,605,606],{},"\u002Fv1\u002Fusers","):",[534,609,610,623],{},[537,611,612],{},[540,613,614,617,620],{},[543,615,616],{},"Method",[543,618,619],{},"Path",[543,621,622],{},"Purpose",[550,624,625,640],{},[540,626,627,632,637],{},[555,628,629],{},[202,630,631],{},"GET",[555,633,634],{},[202,635,636],{},"\u002Fv1\u002Fusers\u002Fme\u002Fsocial-accounts",[555,638,639],{},"List linked providers",[540,641,642,647,652],{},[555,643,644],{},[202,645,646],{},"DELETE",[555,648,649],{},[202,650,651],{},"\u002Fv1\u002Fusers\u002Fme\u002Fsocial-accounts\u002F{account_id}",[555,653,654],{},"Unlink (blocked if it would remove the last auth method)",[195,656,657,658,661],{},"Associate flow (authenticated link) uses ",[202,659,660],{},"get_oauth_associate_router",", not these\nlist\u002Funlink routes.",[214,663,665],{"id":664},"provider-token-storage-optional","Provider token storage (optional)",[195,667,668,669,671],{},"By default provider access\u002Frefresh tokens are ",[259,670,261],{}," stored. To persist them for\ncalling the provider API later:",[264,673,675],{"className":266,"code":674,"language":268,"meta":269,"style":269},"auth = EnterpriseRBAC(\n    ...,\n    store_oauth_provider_tokens=True,\n    oauth_token_encryption_key=os.environ[\"OAUTH_TOKEN_ENCRYPTION_KEY\"],  # Fernet\n)\n",[202,676,677,682,687,692,697],{"__ignoreMap":269},[273,678,679],{"class":275,"line":276},[273,680,681],{},"auth = EnterpriseRBAC(\n",[273,683,684],{"class":275,"line":282},[273,685,686],{},"    ...,\n",[273,688,689],{"class":275,"line":288},[273,690,691],{},"    store_oauth_provider_tokens=True,\n",[273,693,694],{"class":275,"line":295},[273,695,696],{},"    oauth_token_encryption_key=os.environ[\"OAUTH_TOKEN_ENCRYPTION_KEY\"],  # Fernet\n",[273,698,699],{"class":275,"line":301},[273,700,316],{},[195,702,703],{},"Enabling storage without an encryption key fails at construction.",[214,705,707],{"id":706},"security-checklist","Security checklist",[219,709,710,716,722,729,735],{},[222,711,712,713,715],{},"Prefer ",[202,714,559],{}," unless you intentionally allow social signup",[222,717,718,719,721],{},"Only set ",[202,720,569],{}," when you trust provider email verification",[222,723,724,725,728],{},"Use HTTPS + ",[202,726,727],{},"cookie_secure=True"," in production",[222,730,731,732,734],{},"Keep ",[202,733,589],{}," high-entropy and stable across instances",[222,736,737],{},"OutlabsAuth UI can manage linked accounts when the users router is mounted; login\nUX for the OAuth redirect still lives in your product frontend",[214,739,741],{"id":740},"related","Related",[219,743,744,749,754,758],{},[222,745,746],{},[207,747,748],{"href":42},"02-Routers-and-Prefixes.md",[222,750,751],{},[207,752,753],{"href":78},"05-Sessions-and-Audit.md",[222,755,756],{},[207,757,72],{"href":73},[222,759,760],{},[207,761,762],{"href":150},[202,763,764],{},"docs\u002FAUTH_UI.md",[766,767,768],"style",{},"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);}",{"title":269,"searchDepth":276,"depth":282,"links":770},[771,772,775,776,777,778,779],{"id":216,"depth":282,"text":217},{"id":253,"depth":282,"text":254,"children":773},[774],{"id":499,"depth":288,"text":500},{"id":531,"depth":282,"text":532},{"id":595,"depth":282,"text":596},{"id":664,"depth":282,"text":665},{"id":706,"depth":282,"text":707},{"id":740,"depth":282,"text":741},"Provider routers, invite-only login, link and unlink.","md",null,{},{"icon":70},{"title":67,"description":780},"f2gpfZ6NWN8IXXduDbKiPWZeBdLA_BDu9rSfKKJ3Qnk",[788,790],{"title":56,"path":57,"stem":58,"description":789,"icon":59,"children":-1},"Operate OutlabsAuth without a UI, from a terminal or coding agent.",{"title":72,"path":73,"stem":74,"description":791,"icon":75,"children":-1},"One mount, several first-party frontends — profiles, resolution, and sign-in gating.",1787472744721]