text: (self.webpackChunkdocusaurus_template=self.webpackChunkdocusaurus_template||[]).push([[64248],{76910:function(e,n,t){"use strict";t.d(n,{w:function(){return a},T:function(){return o}});var a={browser:{label:"Browser UI",image:t(69885).Z,alt:"User Login HTML Form with validation errors"},missing:{label:"Missing Email",language:"shell",code:t(53009).Z},wrong:{label:"Wrong Credentials",language:"shell",code:t(2745).Z}},o={missing:{label:"Missing ID Token",language:"shell",code:t(89954).Z}}},58215:function(e,n,t){"use strict";var a=t(67294);n.Z=function(e){var n=e.children,t=e.hidden,o=e.className;return a.createElement("div",{role:"tabpanel",hidden:t,className:o},n)}},26396:function(e,n,t){"use strict";t.d(n,{Z:function(){return u}});var a=t(87462),o=t(67294),i=t(72389),r=t(79443);var s=function(){var e=(0,o.useContext)(r.Z);if(null==e)throw new Error('"useUserPreferencesContext" is used outside of "Layout" component.');return e},l=t(89521),c=t(86010),d="tabItem_1uMI";function p(e){var n,t,a,i=e.lazy,r=e.block,p=e.defaultValue,u=e.values,m=e.groupId,f=e.className,h=o.Children.map(e.children,(function(e){if((0,o.isValidElement)(e)&&void 0!==e.props.value)return e;throw new Error("Docusaurus error: Bad <Tabs> child <"+("string"==typeof e.type?e.type:e.type.name)+'>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.')})),g=null!=u?u:h.map((function(e){var n=e.props;return{value:n.value,label:n.label}})),w=(0,l.lx)(g,(function(e,n){return e.value===n.value}));if(w.length>0)throw new Error('Docusaurus error: Duplicate values "'+w.map((function(e){return e.value})).join(", ")+'" found in <Tabs>. Every value needs to be unique.');var v=null===p?p:null!=(n=null!=p?p:null==(t=h.find((function(e){return e.props.default})))?void 0:t.props.value)?n:null==(a=h[0])?void 0:a.props.value;if(null!==v&&!g.some((function(e){return e.value===v})))throw new Error('Docusaurus error: The <Tabs> has a defaultValue "'+v+'" but none of its children has the corresponding value. Available values are: '+g.map((function(e){return e.value})).join(", ")+". If you intend to show no default tab, use defaultValue={null} instead.");var k=s(),b=k.tabGroupChoices,y=k.setTabGroupChoices,N=(0,o.useState)(v),T=N[0],x=N[1],S=[],j=(0,l.o5)().blockElementScrollPositionUntilNextRender;if(null!=m){var E=b[m];null!=E&&E!==T&&g.some((function(e){return e.value===E}))&&x(E)}var F=function(e){var n=e.currentTarget,t=S.indexOf(n),a=g[t].value;a!==T&&(j(n),x(a),null!=m&&y(m,a))},P=function(e){var n,t=null;switch(e.key){case"ArrowRight":var a=S.indexOf(e.currentTarget)+1;t=S[a]||S[0];break;case"ArrowLeft":var o=S.indexOf(e.currentTarget)-1;t=S[o]||S[S.length-1]}null==(n=t)||n.focus()};return o.createElement("div",{className:"tabs-container"},o.createElement("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,c.Z)("tabs",{"tabs--block":r},f)},g.map((function(e){var n=e.value,t=e.label;return o.createElement("li",{role:"tab",tabIndex:T===n?0:-1,"aria-selected":T===n,className:(0,c.Z)("tabs__item",d,{"tabs__item--active":T===n}),key:n,ref:function(e){return S.push(e)},onKeyDown:P,onFocus:F,onClick:F},null!=t?t:n)}))),i?(0,o.cloneElement)(h.filter((function(e){return e.props.value===T}))[0],{className:"margin-vert--md"}):o.createElement("div",{className:"margin-vert--md"},h.map((function(e,n){return(0,o.cloneElement)(e,{key:n,hidden:e.props.value!==T})}))))}function u(e){var n=(0,i.Z)();return o.createElement(p,(0,a.Z)({key:String(n)},e))}},79443:function(e,n,t){"use strict";var a=(0,t(67294).createContext)(void 0);n.Z=a},88821:function(e,n,t){"use strict";var a=t(67294);n.Z=function(){return a.createElement("div",{className:"admonition admonition-warning alert alert--danger"},a.createElement("div",{className:"admonition-heading"},a.createElement("h5",null,a.createElement("span",{className:"admonition-icon"},a.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:"12",height:"16",viewBox:"0 0 12 16"},a.createElement("path",{fillRule:"evenodd",d:"M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"}))),"warning")),a.createElement("div",{className:"admonition-content"},a.createElement("p",null,"Never use API flows to implement Browser applications! Using API flows in Single-Page-Apps as well as server-side apps opens up several potential attack vectors, including Login and other CSRF attacks.")))}},12669:function(e,n,t){"use strict";var a=t(26396),o=t(58215),i=t(67294),r=t(19055),s=t(52387),l=t(25108),c=function(e){var n=e.item;return n.code?(l.warn(n.code,"asfd"),i.createElement(r.Z,{className:"language-"+n.language,children:n.code})):n.image?i.createElement("img",{src:n.image,alt:n.alt}):n.codeFromRemote?i.createElement(s.Z,{language:n.language,link:n.codeFromRemote.link,src:n.codeFromRemote.src}):i.createElement("span",null,"Unknown item type: $",JSON.stringify(n))};n.Z=function(e){var n=e.items;return i.createElement(a.Z,{defaultValue:Object.keys(n)[0],values:Object.keys(n).map((function(e){return{label:n[e].label,value:e}}))},Object.keys(n).map((function(e){return i.createElement(o.Z,{key:e,value:e},i.createElement(c,{item:n[e]}))})))}},52387:function(e,n,t){"use strict";t.d(n,{Z:function(){return d}});var a=t(67294),o=t(83300),i=t.n(o),r=t(19055),s="container_2x1S",l=t(25108),c=function(e,n){if(!e)return 0;var t=n.findIndex((function(n){return n.indexOf(e)>-1}));return-1===t?0:t},d=function(e){var n=e.src,t=e.title,o=(0,a.useState)(""),d=o[0],p=o[1];(0,a.useEffect)((function(){var t,a,o;i()(n.replace("github.com","raw.githubusercontent.com").replace("/blob/","/")).then((function(e){return e.text()})).then((t=e,a=t.startAt,o=t.endAt,function(e){var n=e.split("\n"),t=c(a,n);t>0&&(n=["// ..."].concat(n.slice(t,-1)));var i=c(o,n);return i>0&&(n=[].concat(n.slice(0,i+1),["// ..."])),n.join("\n")})).then(p).catch(l.error)}),[]);var u="language-"+function(e){var n=e.split(".").pop();switch(n){case"jsx":return"jsx";case"tsx":return"tsx";case"ts":return"typescript";case"go":return"go";case"yaml":case"yml":return"yaml";case"js":return"javascript";case"html":return"html";case"pug":return"pug";default:return n}}(n),m='title="'+(t||function(e){var n=e.match(new RegExp("https://github.com/[^/]+/[^/]+/blob/[^/]+/(.+)","i"))||[];return n.length>=2?n[1]:e}(n))+'"';return a.createElement("div",{className:s},a.createElement(r.Z,{metastring:m,className:u,children:d}))}},92256:function(e,n,t){"use strict";var a=t(67294);n.Z=function(e){var n=e.severity,t=void 0===n?"info":n;return a.createElement("div",{className:"admonition admonition-warning alert alert--"+t},a.createElement("div",{className:"admonition-heading"},a.createElement("h5",null,a.createElement("span",{className:"admonition-icon"},a.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:"12",height:"16",viewBox:"0 0 12 16"},a.createElement("path",{fillRule:"evenodd",d:"M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"}))),"severity")),a.createElement("div",{className:"admonition-content"},a.createElement("p",null,"Ory Kratos and your UI must be on the hosted on same top level domain! You can not host Ory Kratos and your UI on separate top level domains:"),a.createElement("ul",null,a.createElement("li",null,a.createElement("code",null,"kratos.bar.com")," and ",a.createElement("code",null,"ui.bar.com")," will work;"),a.createElement("li",null,a.createElement("code",null,"kratos.bar.com")," and ",a.createElement("code",null,"bar.com")," will work;"),a.createElement("li",null,a.createElement("code",null,"kratos.bar.com")," and ",a.createElement("code",null,"not-ar.com")," will not work."))))}},59079:function(e,n,t){"use strict";t.d(n,{Z:function(){return m}});var a=t(67294),o=t(21140),i=t.n(o),r="graph_1lrJ",s="pointer_3d8u",l="overlay_2fuY",c="visible_2Z3U",d="backdrop_2z5L",p=t(94184),u=t.n(p);i().initialize({startOnLoad:!0,logLevel:"fatal",securityLevel:"strict",arrowMarkerAbsolute:!1,theme:"neutral",flowchart:{useMaxWidth:!0,htmlLabels:!0,rankSpacing:65,nodeSpacing:30,curve:"basis"},sequence:{useMaxWidth:!0},gantt:{useMaxWidth:!0}});var m=function(e){var n,t=e.chart,o=(0,a.useState)(!1),p=o[0],m=o[1],f=(0,a.useState)(void 0),h=f[0],g=f[1],w=(0,a.useState)("mermaid-"+Math.random().toString(36).substr(2,-1))[0],v=function(){return m(!p)};return(0,a.useEffect)((function(){i().render(w,t,(function(e){g(e)}))}),[]),a.createElement(a.Fragment,null,a.createElement("div",{onClick:v,className:u()(r,s),dangerouslySetInnerHTML:{__html:h}}),a.createElement("div",{onClick:v,className:u()(l,s,r,(n={},n[c]=p,n))},a.createElement("div",{onClick:function(e){return e.stopPropagation()},className:u()(d,r),dangerouslySetInnerHTML:{__html:h}})))}},19582:function(e,n,t){"use strict";var a=t(67294),o=t(59079);n.Z=function(e){return a.createElement(o.Z,{chart:(n=e,t=n.flows,i=void 0===t?["login","registration","settings","..."]:t,r=n.interactions,s=void 0===r?['"Log in"','"Sign Up"','"Update Email"',"..."]:r,l=n.success,c=void 0===l?"Perform flow-specific action (e.g. create user, set session cookie, ...)":l,d=i.length>1?"<"+i.join("|")+">":""+i.join("|"),"\nsequenceDiagram\n  participant B as API Client\n  participant K as Ory Kratos\n\n  B->>K: REST GET /self-service/"+d+"/api\n  K--\x3e>K: Create and store new "+i.join(", ")+" flow\n  K->>B: HTTP 200 OK with flow as application/json payload\n  B--\x3e>B: Render form using e.g. Native iOS UI Elements\n  B--\x3e>B: User fills out forms, clicks e.g. "+s+"\n  B->>K: REST POST to e.g. /self-service/"+d+"?flow=...>\n  K--\x3e>K: Validates and processes payload\n  alt Form payload is valid\n    K->>B: "+c+"\n  else Form payload invalid\n    K--\x3e>K: Update and store flow (e.g. add form validation errors)\n    K->>B: Respond with e.g. HTTP 400 Bad Request and updated flow as payload\n    B--\x3e>B: Render form and validation errors using e.g. Native iOS UI Elements\n    B--\x3e>K: Repeat flow with input data, submit, validate, ...\n  end\n")});var n,t,i,r,s,l,c,d}},64923:function(e,n,t){"use strict";var a=t(67294),o=t(59079);n.Z=function(e){return a.createElement(a.Fragment,null,a.createElement(o.Z,{chart:(n=e,t=n.flows,i=void 0===t?["login","registration","settings","..."]:t,r=n.interactions,s=void 0===r?['"Log in"','"Sign Up"','"Update Email"',"..."]:r,l=n.success,c=void 0===l?"Perform flow-specific action (e.g. create user, set session cookie, ...)":l,d=i.length>1?"<"+i.join("|")+">":""+i.join("|"),"\nsequenceDiagram\n\n  participant B as Browser\n  participant K as Ory Kratos\n  participant A as Flow UI\n\n  B->>K: Follow link to /self-service/"+d+"/browser\n  K--\x3e>K: Create and store new "+i.join(", ")+" flow\n  K->>B: HTTP 302 Found <selfservice.flows."+d+".ui_url>?flow=<flow-id>\n\n  B->>A: Opens <selfservice.flows.<"+i.join("|")+">.ui_url>?flow=<flow-id>\n  A--\x3e>K: Fetches data to render forms using /selfservice/"+d+"/flows?id=<flow-id>\n  B--\x3e>A: Fills out forms, clicks e.g. "+s.join(", ")+"\n  B->>K: Submits form\n  K--\x3e>K: Validates and processes form payloads\n\n  alt Form payload is valid\n    K->>B: "+c+"\n  else Form payload invalid\n    K--\x3e>K: Update and store flow (e.g. add form validation errors)\n    K--\x3e>B: HTTP 302 Found <selfservice.flows."+d+".ui_url>?flow=<flow-id>\n    B->>A: Opens <selfservice.flows."+d+"?flow=<flow-id>\n    A--\x3e>K: Fetches data to render form fields and errors\n    B->>K: Repeat flow with input data, submit, validate, ...\n  end\n")}),a.createElement("p",null,"The ",a.createElement("em",null,"Flow UI")," (",a.createElement("strong",null,"your application!"),') is responsible for rendering the actual Login and Registration HTML Forms. You can of course implement one app for rendering all the Login, Registration, ... screens, and another app (think "Service Oriented Architecture", "Micro-Services" or "Service Mesh") is responsible for rendering your Dashboards, Management Screens, and so on.'));var n,t,i,r,s,l,c,d}},44212:function(e,n,t){"use strict";var a=t(67294),o=t(59079);n.Z=function(e){return a.createElement(o.Z,{chart:(n=e,t=n.flows,i=void 0===t?["login","registration","settings","..."]:t,r=n.interactions,s=void 0===r?['"Log in"','"Sign Up"','"Update Email"',"..."]:r,l=n.success,c=void 0===l?"Perform flow-specific action (e.g. create user, set session cookie, ...)":l,d=i.length>1?"<"+i.join("|")+">":""+i.join("|"),"\nsequenceDiagram\n  participant B as AJAX Client\n  participant K as Ory Kratos\n\n  B->>K: REST GET /self-service/"+d+"/browser\n  K--\x3e>K: Create and store new "+i.join(", ")+" flow\n  K->>B: HTTP 200 OK with flow as application/json payload\n  B--\x3e>B: Render form using HTML input elements\n  B--\x3e>B: User fills out forms, clicks e.g. "+s+"\n  B->>K: REST POST to e.g. /self-service/"+d+"?flow=...>\n  K--\x3e>K: Validates and processes payload\n  alt Form payload is valid\n    K->>B: "+c+"\n  else Form payload invalid\n    K--\x3e>K: Update and store flow (e.g. add form validation errors)\n    K->>B: Respond with e.g. HTTP 400 Bad Request and updated flow as payload\n    B--\x3e>B: Render form and validation errors using HTML input elements\n    B--\x3e>K: Repeat flow with input data, submit, validate, ...\n  end\n")});var n,t,i,r,s,l,c,d}},59961:function(e,n,t){"use strict";t.r(n),t.d(n,{frontMatter:function(){return u},contentTitle:function(){return m},metadata:function(){return f},toc:function(){return h},default:function(){return w}});var a=t(87462),o=t(63366),i=(t(67294),t(3905)),r=t(88821),s=(t(12669),t(76910),t(64923)),l=t(19582),c=t(44212),d=t(92256),p=["components"],u={id:"self-service",title:"Self-Service Flows",sidebar_label:"Concepts and Overview"},m=void 0,f={unversionedId:"self-service",id:"self-service",isDocsHomePage:!1,title:"Self-Service Flows",description:"Ory Kratos implements flows that users perform themselves as opposed to",source:"@site/docs/self-service.mdx",sourceDirName:".",slug:"/self-service",permalink:"/kratos/docs/next/self-service",editUrl:"https://github.com/ory/kratos/edit/master/docs/docs/self-service.mdx",tags:[],version:"current",lastUpdatedBy:"aeneasr",lastUpdatedAt:1634626107,formattedLastUpdatedAt:"10/19/2021",frontMatter:{id:"self-service",title:"Self-Service Flows",sidebar_label:"Concepts and Overview"},sidebar:"docs",previous:{title:"Threat Models and Security Profiles",permalink:"/kratos/docs/next/concepts/security"},next:{title:"User Registration",permalink:"/kratos/docs/next/self-service/flows/user-registration"}},h=[{value:"Performing Login, Registration, Settings, ... Flows",id:"performing-login-registration-settings--flows",children:[],level:2},{value:"Browser Flows for Server-Side Apps: NodeJS, PHP, Java, ...",id:"browser-flows-for-server-side-apps-nodejs-php-java-",children:[{value:"Initialization and Redirect to UI",id:"initialization-and-redirect-to-ui",children:[],level:3},{value:"Form Rendering",id:"form-rendering",children:[],level:3},{value:"Form Submission and Payload Validation",id:"form-submission-and-payload-validation",children:[],level:3}],level:2},{value:"Browser Flows for Client-Side Apps: Single-Page-Apps, ReactJS, AngularJS, NextJS, ...",id:"browser-flows-for-client-side-apps-single-page-apps-reactjs-angularjs-nextjs-",children:[{value:"Initialization",id:"initialization",children:[],level:3},{value:"Form Rendering",id:"form-rendering-1",children:[],level:3},{value:"Form Submission and Payload Validation",id:"form-submission-and-payload-validation-1",children:[],level:3}],level:2},{value:"API Flows",id:"api-flows",children:[{value:"Initialization",id:"initialization-1",children:[],level:3},{value:"Form Rendering",id:"form-rendering-2",children:[],level:3},{value:"Form Submission and Payload Validation",id:"form-submission-and-payload-validation-2",children:[],level:3}],level:2}],g={toc:h};function w(e){var n=e.components,t=(0,o.Z)(e,p);return(0,i.kt)("wrapper",(0,a.Z)({},g,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"Ory Kratos implements flows that users perform themselves as opposed to\nadministrative intervention. Facebook and Google both provide self-service\nregistration and profile management features as you are able to make changes to\nyour profile and sign up yourself."),(0,i.kt)("p",null,"Most believe that user management systems are easy to implement because picking\nthe right password hashing algorithm and sending an account verification code is\na solvable challenge. The real complexity however hides in the details and\nattack vectors of self-service flows. Most data leaks happen because someone is\nable to exploit"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"registration: with attack vectors such as account enumeration, ...;"),(0,i.kt)("li",{parentName:"ul"},"login: phishing, account enumeration, leaked password databases, brute-force,\n...;"),(0,i.kt)("li",{parentName:"ul"},"user settings: account enumeration, account takeover, ...;"),(0,i.kt)("li",{parentName:"ul"},"account recovery: social engineering attacks, account takeover, spoofing, and\nso on.")),(0,i.kt)("p",null,"There are also many other areas you need to think about, such as:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Mobile, Smart TV, ... authentication"),(0,i.kt)("li",{parentName:"ul"},'Linking and unlinking social profiles (e.g. "Sign in with Google" or "Connect\nwith Google") to existing accounts')),(0,i.kt)("p",null,"Ory Kratos applies best practices established by experts (National Institute of\nSciences NIST, Internet Engineering Task Force IETF, Microsoft Research, Google\nResearch, Troy Hunt, ...) and implements the following flows:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/kratos/docs/next/self-service/flows/user-registration"},"Registration")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/kratos/docs/next/self-service/flows/user-login"},"Login")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/kratos/docs/next/self-service/flows/user-logout"},"Logout")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/kratos/docs/next/self-service/flows/user-settings"},"User Settings")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/kratos/docs/next/self-service/flows/account-recovery"},"Account Recovery")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/kratos/docs/next/self-service/flows/verify-email-account-activation"},"Address Verification")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/kratos/docs/next/self-service/flows/user-facing-errors"},"User-Facing Error")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/kratos/docs/next/self-service/flows/2fa-mfa-multi-factor-authentication"},"2FA / MFA"))),(0,i.kt)("p",null,'Some flows break down into "flow methods" which implement some of the flow\'s\nbusiness logic:'),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"password"),' method implements the login and registration with "email or/and\nusername and password" method, and "change your password" user settings\nmethod.'),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"oidc"),' (OpenID Connect, OAuth2, Social Sign In) method implements "Sign in\nwith ..." login and registration method and "un/link another social account"\nuser settings method.'),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"profile"),' method implements the "update your profile", "change your\nfirst/last name, ..." user settings method).'),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"link"),' method implements the "click this link to reset your password"\naccount recovery method.')),(0,i.kt)("p",null,"Some flows additionally implement the ability\n",(0,i.kt)("a",{parentName:"p",href:"/kratos/docs/next/self-service/hooks"},"to run hooks")," which allow users to be immediately\nsigned in after registration, notify another system on successful registration\n(e.g. Mailchimp), and so on."),(0,i.kt)("h2",{id:"performing-login-registration-settings--flows"},"Performing Login, Registration, Settings, ... Flows"),(0,i.kt)("p",null,"There are two flow types supported in Ory Kratos:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Flows where the user sits in front of the Browser and the application is",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"a server-side application (e.g. NodeJS, Java, ...)"),(0,i.kt)("li",{parentName:"ul"},"a client-side application (e.g. ReactJS, AngularJS, ...)"))),(0,i.kt)("li",{parentName:"ul"},"Flows where API interaction is required (e.g. mobile app, Smart TV, ...)")),(0,i.kt)("p",null,"All Self-Service Flows (",(0,i.kt)("a",{parentName:"p",href:"/kratos/docs/next/self-service/flows/user-login"},"User Login"),",\n",(0,i.kt)("a",{parentName:"p",href:"/kratos/docs/next/self-service/flows/user-registration"},"User Registration"),",\n",(0,i.kt)("a",{parentName:"p",href:"/kratos/docs/next/self-service/flows/user-settings"},"Profile Management"),",\n",(0,i.kt)("a",{parentName:"p",href:"/kratos/docs/next/self-service/flows/account-recovery"},"Account Recovery"),",\n",(0,i.kt)("a",{parentName:"p",href:"/kratos/docs/next/self-service/flows/verify-email-account-activation"},"Email or Phone verification"),")\nsupport these two flow types and use the same data models but do use different\nAPI endpoints."),(0,i.kt)(r.Z,{mdxType:"ApiWarning"}),(0,i.kt)("h2",{id:"browser-flows-for-server-side-apps-nodejs-php-java-"},"Browser Flows for Server-Side Apps: NodeJS, PHP, Java, ..."),(0,i.kt)("p",null,"Browser-based flows for server-side apps make use of three core HTTP\ntechnologies:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"HTTP Redirects"),(0,i.kt)("li",{parentName:"ul"},"HTTP POST (",(0,i.kt)("inlineCode",{parentName:"li"},"application/x-www-urlencoded"),") and REST GET requests."),(0,i.kt)("li",{parentName:"ul"},"HTTP Cookies to prevent CSRF and Session Hijacking attack vectors.")),(0,i.kt)("p",null,"Ory Kratos takes care of all required session and CSRF cookies and ensures that\nall security requirements are fulfilled."),(0,i.kt)("p",null,"The browser flow has three stages:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Initialization and redirect to UI;"),(0,i.kt)("li",{parentName:"ul"},"Form rendering;"),(0,i.kt)("li",{parentName:"ul"},"Form submission and payload validation.")),(0,i.kt)("div",{className:"admonition admonition-note alert alert--secondary"},(0,i.kt)("div",{parentName:"div",className:"admonition-heading"},(0,i.kt)("h5",{parentName:"div"},(0,i.kt)("span",{parentName:"h5",className:"admonition-icon"},(0,i.kt)("svg",{parentName:"span",xmlns:"http://www.w3.org/2000/svg",width:"14",height:"16",viewBox:"0 0 14 16"},(0,i.kt)("path",{parentName:"svg",fillRule:"evenodd",d:"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"}))),"note")),(0,i.kt)("div",{parentName:"div",className:"admonition-content"},(0,i.kt)("p",{parentName:"div"},"All examples use the Ory Cloud Platform (OCP) playground project. Please be\naware that everyone can see the data you enter there. You can replace the OCP\nURL with a locally hosted Ory Kratos as well."))),(0,i.kt)(s.Z,{flows:["login","settings","..."],mdxType:"SelfServiceBrowserFlow"}),(0,i.kt)("h3",{id:"initialization-and-redirect-to-ui"},"Initialization and Redirect to UI"),(0,i.kt)("p",null,"First, the Browser opens the flow's initialization endpoint\n(e.g.",(0,i.kt)("inlineCode",{parentName:"p"},"/self-service/login/browser"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"/self-service/registration/browser"),", ...):"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-shell-session"},'$ curl -s -i -X GET \\\n    -H "Accept: text/html" \\\n    https://playground.projects.oryapis.com/api/kratos/public/self-service/login/browser\n\nHTTP/2 303\ndate: Fri, 09 Jul 2021 10:23:52 GMT\ncontent-type: text/html; charset=utf-8\ncontent-length: 121\nlocation: https://playground.projects.oryapis.com/hosted/login?flow=3fc63726-8461-43f4-974a-5579ff4174f1\ncache-control: private, no-cache, no-store, must-revalidate\nset-cookie: aHR0cHM6Ly9wbGF5Z3JvdW5kLnByb2plY3RzLm9yeWFwaXMuY29tL2FwaS9rcmF0b3MvcHVibGlj_csrf_token=Lk9swSOlimGS9LI5HslOyEKGL4hMQzWHnwnQpm9HGAA=; Path=/api/kratos/public; Domain=playground.projects.oryapis.com; Max-Age=31536000; HttpOnly; Secure; SameSite=None\nvary: Origin\nvary: Cookie\nstrict-transport-security: max-age=15724800; includeSubDomains\n\n<a href="https://playground.projects.oryapis.com/hosted/login?flow=3fc63726-8461-43f4-974a-5579ff4174f1">See Other</a>.\n')),(0,i.kt)("p",null,'The initialization endpoint creates a flow object and stores it in the database.\nThe flow object has an ID and contains additional information about the flow\nsuch as the login methods (e.g. "username/password" and "Sign in with Google")\nand their form data.'),(0,i.kt)(d.Z,{mdxType:"DomainWarning"}),(0,i.kt)("p",null,"Once stored, the Browser is HTTP 302 redirected to the flow's configured UI URL\n(e.g. ",(0,i.kt)("inlineCode",{parentName:"p"},"selfservice.flows.login.ui_url"),"), appending the flow ID as the ",(0,i.kt)("inlineCode",{parentName:"p"},"flow")," URL\nQuery Parameter. Also included are HTTP cookies such as Anti-CSRF cookies."),(0,i.kt)("h3",{id:"form-rendering"},"Form Rendering"),(0,i.kt)("p",null,"The Browser follows the link and opens"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"https://playground.projects.oryapis.com/hosted/login?flow=36c3246f-8e40-4f45-8721-c246576a8bc3\n")),(0,i.kt)("p",null,'which renders the HTML form which for example shows the "username and password"\nfield, the "Update your email address" field, or other flow forms. This HTML\nform is rendered be the ',(0,i.kt)("a",{parentName:"p",href:"/kratos/docs/next/concepts/ui-user-interface"},"Self-Service UI")," which\nyou fully control."),(0,i.kt)("p",null,"The endpoint responsible for the UI URL uses the ",(0,i.kt)("inlineCode",{parentName:"p"},"flow")," URL Query Parameter\n(here ",(0,i.kt)("inlineCode",{parentName:"p"},"...?flow=36c3246f-8e40-4f45-8721-c246576a8bc3"),") to call the flow\ninformation endpoint, for example:\n",(0,i.kt)("inlineCode",{parentName:"p"},"https://playground.projects.oryapis.com/api/kratos/public/self-service/login/flows?id=36c3246f-8e40-4f45-8721-c246576a8bc3")),(0,i.kt)("div",{className:"admonition admonition-note alert alert--secondary"},(0,i.kt)("div",{parentName:"div",className:"admonition-heading"},(0,i.kt)("h5",{parentName:"div"},(0,i.kt)("span",{parentName:"h5",className:"admonition-icon"},(0,i.kt)("svg",{parentName:"span",xmlns:"http://www.w3.org/2000/svg",width:"14",height:"16",viewBox:"0 0 14 16"},(0,i.kt)("path",{parentName:"svg",fillRule:"evenodd",d:"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"}))),"note")),(0,i.kt)("div",{parentName:"div",className:"admonition-content"},(0,i.kt)("p",{parentName:"div"},"Calling the flow enpdoint without the correct CSRF cookies will result in a\nfailure!"))),(0,i.kt)("p",null,"The endpoint returns the flow data - so for example the login form and any\nvalidation errors. Let's take a look at an example using CURL:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-shell-session"},'# A cookie jar for storing the CSRF tokens\n$ cookieJar=$(mktemp)\n$ flowId=$(curl -s -X GET \\\n    --cookie-jar $cookieJar --cookie $cookieJar \\\n    -H "Accept: application/json" \\\n    https://playground.projects.oryapis.com/api/kratos/public/self-service/login/browser | jq -r \'.id\')\n\n# The endpoint uses Ory Kratos\' REST API to fetch information about the request\n$ curl -s -X GET \\\n    --cookie-jar $cookieJar --cookie $cookieJar \\\n    -H "Accept: application/json" \\\n    "https://playground.projects.oryapis.com/api/kratos/public/self-service/login/flows?id=$flowId" | jq\n')),(0,i.kt)("p",null,"The response includes login methods, their fields, and additional information\nabout the flow:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json"},'{\n  "id": "33f6079a-ef14-4084-af13-34a91e53cd6c",\n  "type": "browser",\n  "expires_at": "2021-07-09T10:08:08.856735Z",\n  "issued_at": "2021-07-09T09:08:08.856735Z",\n  "request_url": "http://playground.projects.oryapis.com/self-service/login/browser",\n  "ui": {\n    "action": "https://playground.projects.oryapis.com/api/kratos/public/self-service/login?flow=33f6079a-ef14-4084-af13-34a91e53cd6c",\n    "method": "POST",\n    "nodes": [\n      {\n        "type": "input",\n        "group": "default",\n        "attributes": {\n          "name": "csrf_token",\n          "type": "hidden",\n          "value": "XmG3qwTYSV0oWIyNGTugvtNOKMxWPYHd7dNX7BYK5lL79P0iUdq5jVmRUKwwm8RLcAGN7eF7iYraAiTSOdamuQ==",\n          "required": true,\n          "disabled": false\n        },\n        "messages": [],\n        "meta": {}\n      },\n      {\n        "type": "input",\n        "group": "password",\n        "attributes": {\n          "name": "password_identifier",\n          "type": "text",\n          "value": "",\n          "required": true,\n          "disabled": false\n        },\n        "messages": [],\n        "meta": {\n          "label": {\n            "id": 1070004,\n            "text": "ID",\n            "type": "info"\n          }\n        }\n      },\n      {\n        "type": "input",\n        "group": "password",\n        "attributes": {\n          "name": "password",\n          "type": "password",\n          "required": true,\n          "disabled": false\n        },\n        "messages": [],\n        "meta": {\n          "label": {\n            "id": 1070001,\n            "text": "Password",\n            "type": "info"\n          }\n        }\n      },\n      {\n        "type": "input",\n        "group": "password",\n        "attributes": {\n          "name": "method",\n          "type": "submit",\n          "value": "password",\n          "disabled": false\n        },\n        "messages": [],\n        "meta": {\n          "label": {\n            "id": 1010001,\n            "text": "Sign in",\n            "type": "info",\n            "context": {}\n          }\n        }\n      }\n    ],\n    "messages": [\n      {\n        "id": 4010002,\n        "text": "Could not find a strategy to log you in with. Did you fill out the form correctly?",\n        "type": "error"\n      }\n    ]\n  },\n  "created_at": "2021-07-09T09:08:08.857959Z",\n  "updated_at": "2021-07-09T09:08:08.857959Z",\n  "forced": false\n}\n')),(0,i.kt)("p",null,"For more details, check out the individual flow documentation."),(0,i.kt)("p",null,"The flow UI then renders the given methods. For the example above, a suitable\nHTML Form would look along the lines of:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-html"},'<form\n  action="https://playground.projects.oryapis.com/api/kratos/public/self-service/login?flow=f6209031-38d5-48bc-b6a5-118e8a24d1fe"\n  method="POST"\n>\n  <input\n    name="csrf_token"\n    type="hidden"\n    value="XmG3qwTYSV0oWIyNGTugvtNOKMxWPYHd7dNX7BYK5lL79P0iUdq5jVmRUKwwm8RLcAGN7eF7iYraAiTSOdamuQ=="\n  />\n  <fieldset>\n    <label>\n      <input name="password_identifier" type="text" value="" placeholder="ID" />\n      <span>ID</span>\n    </label>\n  </fieldset>\n  <fieldset>\n    <label>\n      <input name="password" type="password" value="" placeholder="Password" />\n      <span>Password</span>\n    </label>\n  </fieldset>\n  <button name="method" type="submit" value="password">Sign in</button>\n</form>\n')),(0,i.kt)("h3",{id:"form-submission-and-payload-validation"},"Form Submission and Payload Validation"),(0,i.kt)("p",null,"The user completes the flow by submitting the form. The form action always\npoints to Ory Kratos directly. The business logic for the flow method will then\nvalidate the form payload and return to the UI URL on validation errors. The UI\nthen fetches the information about the flow again"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"# The endpoint uses Ory Kratos' REST API to fetch information about the request\n$ theFlowID=...\n$ curl -s -i -X GET \\\n    -H \"Accept: application/json\"  \\\n    'https://playground.projects.oryapis.com/api/kratos/public/self-service/login/flows?id=$theFlowID' | jq\n")),(0,i.kt)("p",null,"which now includes validation errors and other potential messages:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json5"},"{\n  id: '36c3246f-8e40-4f45-8721-c246576a8bc3',\n  type: 'browser',\n  expires_at: '2021-04-28T09:00:57.10254633Z',\n  issued_at: '2021-04-28T08:50:57.10254633Z',\n  request_url: 'https://playground.projects.oryapis.com/api/kratos/public/self-service/login/browser',\n  ui: {\n    action: 'https://playground.projects.oryapis.com/api/kratos/public/self-service/login?flow=36c3246f-8e40-4f45-8721-c246576a8bc3',\n    method: 'POST',\n    nodes: [\n      // ...\n      {\n        type: 'input',\n        group: 'password',\n        attributes: {\n          name: 'password_identifier',\n          type: 'text',\n          value: '',\n          required: true,\n          disabled: false\n        },\n        messages: [\n          {\n            id: 4000001,\n            text: 'length must be >= 1, but got 0',\n            type: 'error'\n          }\n        ],\n        meta: {\n          label: {\n            id: 1070004,\n            text: 'ID',\n            type: 'info'\n          }\n        }\n      },\n      {\n        type: 'input',\n        group: 'password',\n        attributes: {\n          name: 'password',\n          type: 'password',\n          required: true,\n          disabled: false\n        },\n        messages: [\n          {\n            id: 4000001,\n            text: 'length must be >= 1, but got 0',\n            type: 'error'\n          }\n        ],\n        meta: {\n          label: {\n            id: 1070001,\n            text: 'Password',\n            type: 'info'\n          }\n        }\n      }\n      // ...\n    ]\n  },\n  forced: false\n}\n")),(0,i.kt)("p",null,"If a system error (e.g. broken configuration file) occurs, the browser is\nredirected to the ",(0,i.kt)("a",{parentName:"p",href:"/kratos/docs/next/self-service/flows/user-facing-errors"},"Error UI"),"."),(0,i.kt)("p",null,"If the form payload is valid, the flow completes with a success. The result here\ndepends on the flow itself - the login flow for example redirects the user to a\nspecified redirect URL and sets a session cookie. The registration flow also\nredirects to a specified redirect URL but only creates the user in the database\nand might issue a session cookie if configured to do so."),(0,i.kt)("h2",{id:"browser-flows-for-client-side-apps-single-page-apps-reactjs-angularjs-nextjs-"},"Browser Flows for Client-Side Apps: Single-Page-Apps, ReactJS, AngularJS, NextJS, ..."),(0,i.kt)("p",null,"Browser-based flows also support Client Side applications such as\nSingle-Page-Apps (ReactJS, AngularJS, NextJS, ...) that use AJAX to perform\nrequests."),(0,i.kt)("div",{className:"admonition admonition-info alert alert--info"},(0,i.kt)("div",{parentName:"div",className:"admonition-heading"},(0,i.kt)("h5",{parentName:"div"},(0,i.kt)("span",{parentName:"h5",className:"admonition-icon"},(0,i.kt)("svg",{parentName:"span",xmlns:"http://www.w3.org/2000/svg",width:"14",height:"16",viewBox:"0 0 14 16"},(0,i.kt)("path",{parentName:"svg",fillRule:"evenodd",d:"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"}))),"info")),(0,i.kt)("div",{parentName:"div",className:"admonition-content"},(0,i.kt)("p",{parentName:"div"},"Ory Kratos and your UI must be on the hosted on same top level domain! You can\nnot host Ory Kratos and your UI on separate top level domains:"),(0,i.kt)("ul",{parentName:"div"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"kratos.bar.com")," and ",(0,i.kt)("inlineCode",{parentName:"li"},"ui.bar.com")," will work;"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"kratos.bar.com")," and ",(0,i.kt)("inlineCode",{parentName:"li"},"bar.com")," will work;"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"kratos.bar.com")," and ",(0,i.kt)("inlineCode",{parentName:"li"},"not-bar.com")," will not work.")))),(0,i.kt)("p",null,"Ory Kratos identifies SPA requests by checking if the ",(0,i.kt)("inlineCode",{parentName:"p"},"Accept")," HTTP Header is\nset to ",(0,i.kt)("inlineCode",{parentName:"p"},"application/json"),"."),(0,i.kt)("p",null,"If it is, these flows act like an API flow and respond with JSON instead of HTTP\nredirects while still setting and requiring the necessary cookies for CSRF,\nsessions, and more:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Initialization ",(0,i.kt)("strong",{parentName:"li"},"without redirect")," using an AJAX request;"),(0,i.kt)("li",{parentName:"ul"},"Form rendering using HTML;"),(0,i.kt)("li",{parentName:"ul"},"Form submission as ",(0,i.kt)("inlineCode",{parentName:"li"},"application/json")," and payload validation.")),(0,i.kt)("p",null,"The high-level sequence diagram for API flows looks as follows:"),(0,i.kt)(c.Z,{mdxType:"SelfServiceSpaFlow"}),(0,i.kt)("h3",{id:"initialization"},"Initialization"),(0,i.kt)("p",null,"The AJAX client (e.g. NextJS app) makes a request to the flow's initialization\nendpoint (e.g.",(0,i.kt)("inlineCode",{parentName:"p"},"/self-service/login/browser"),",\n",(0,i.kt)("inlineCode",{parentName:"p"},"/self-service/registration/browser"),", ...):"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-shell-session"},'$ curl -v -s -X GET \\\n    -H "Accept: application/json"  \\\n    https://playground.projects.oryapis.com/api/kratos/public/self-service/login/browser | jq\n\n> GET /api/kratos/public/self-service/login/browser HTTP/2\n> Host: playground.projects.oryapis.com\n> User-Agent: curl/7.64.1\n> Accept: application/json\n\n< HTTP/2 200\n< date: Fri, 09 Jul 2021 10:25:12 GMT\n< content-type: application/json; charset=utf-8\n< content-length: 1359\n< cache-control: private, no-cache, no-store, must-revalidate\n< set-cookie: aHR0cHM6Ly9wbGF5Z3JvdW5kLnByb2plY3RzLm9yeWFwaXMuY29tL2FwaS9rcmF0b3MvcHVibGlj_csrf_token=UlKMcLe00G8B9GjC7D1I5rvQ6P79Q0YpzKb4lo7uLtw=; Path=/api/kratos/public; Domain=playground.projects.oryapis.com; Max-Age=31536000; HttpOnly; Secure; SameSite=None\n< vary: Origin\n< vary: Cookie\n< strict-transport-security: max-age=15724800; includeSubDomains\n\n{\n  "id": "ff0c97c4-a7bb-49a5-a8a6-ebf174877fa5",\n  "type": "browser",\n  "expires_at": "2021-07-09T11:25:12.136099226Z",\n  "issued_at": "2021-07-09T10:25:12.136099226Z",\n  "request_url": "http://playground.projects.oryapis.com/self-service/login/browser",\n  "ui": {\n    "action": "https://playground.projects.oryapis.com/api/kratos/public/self-service/login?flow=ff0c97c4-a7bb-49a5-a8a6-ebf174877fa5",\n    "method": "POST",\n    "nodes": [ /* ... */ ]\n  },\n  "created_at": "2021-07-09T10:25:12.137554Z",\n  "updated_at": "2021-07-09T10:25:12.137554Z",\n  "forced": false\n}\n')),(0,i.kt)("p",null,'The initialization endpoint creates a flow object and stores it in the database.\nThe flow object has an ID and contains additional information about the flow\nsuch as the login methods (e.g. "username/password" and "Sign in with Google")\nand their form data.'),(0,i.kt)("h3",{id:"form-rendering-1"},"Form Rendering"),(0,i.kt)("p",null,"Form rendering works the same way as the flows for server-side browser apps."),(0,i.kt)("h3",{id:"form-submission-and-payload-validation-1"},"Form Submission and Payload Validation"),(0,i.kt)("p",null,"To submit the form, it is recommended to intercept the submission and send the\nform as JSON using ",(0,i.kt)("inlineCode",{parentName:"p"},"fetch")," (see\n",(0,i.kt)("a",{parentName:"p",href:"https://dev.to/amjadmh73/submit-html-forms-to-json-apis-easily-137l"},'"Submit HTML Forms to JSON APIs easily"'),")\nor the ",(0,i.kt)("a",{parentName:"p",href:"/kratos/docs/next/sdk"},"SDK"),"."),(0,i.kt)("p",null,"The response depends on the result (sign up success, account recovery success,\nform validation error, ...). If a form validation error occurs, the following\nresponse could be sent:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json5"},"{\n  id: 'f6209031-38d5-48bc-b6a5-118e8a24d1fe',\n  type: 'browser',\n  expires_at: '2021-04-28T09:00:57.10254633Z',\n  issued_at: '2021-04-28T08:50:57.10254633Z',\n  request_url: 'https://playground.projects.oryapis.com/api/kratos/public/self-service/login/browser',\n  ui: {\n    action: 'https://playground.projects.oryapis.com/api/kratos/public/self-service/login?flow=f6209031-38d5-48bc-b6a5-118e8a24d1fe',\n    method: 'POST',\n    nodes: [\n      // ...\n      {\n        type: 'input',\n        group: 'password',\n        attributes: {\n          name: 'password_identifier',\n          type: 'text',\n          value: '',\n          required: true,\n          disabled: false\n        },\n        messages: [\n          {\n            id: 4000001,\n            text: 'length must be >= 1, but got 0',\n            type: 'error'\n          }\n        ],\n        meta: {\n          label: {\n            id: 1070004,\n            text: 'ID',\n            type: 'info'\n          }\n        }\n      },\n      {\n        type: 'input',\n        group: 'password',\n        attributes: {\n          name: 'password',\n          type: 'password',\n          required: true,\n          disabled: false\n        },\n        messages: [\n          {\n            id: 4000001,\n            text: 'length must be >= 1, but got 0',\n            type: 'error'\n          }\n        ],\n        meta: {\n          label: {\n            id: 1070001,\n            text: 'Password',\n            type: 'info'\n          }\n        }\n      }\n      // ...\n    ]\n  },\n  forced: false\n}\n")),(0,i.kt)("h2",{id:"api-flows"},"API Flows"),(0,i.kt)(r.Z,{mdxType:"ApiWarning"}),(0,i.kt)("p",null,"API flows have three stages:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Initialization without redirect and cookies;"),(0,i.kt)("li",{parentName:"ul"},"Form rendering using e.g. native iOS, Android, ... components;"),(0,i.kt)("li",{parentName:"ul"},"Form submission as ",(0,i.kt)("inlineCode",{parentName:"li"},"application/json")," and payload validation.")),(0,i.kt)("p",null,"The high-level sequence diagram for API flows looks as follows:"),(0,i.kt)(l.Z,{mdxType:"SelfServiceApiFlow"}),(0,i.kt)("h3",{id:"initialization-1"},"Initialization"),(0,i.kt)("p",null,"The client (e.g. mobile application) makes a request to the flow's\ninitialization endpoint (e.g.",(0,i.kt)("inlineCode",{parentName:"p"},"/self-service/login/api"),",\n",(0,i.kt)("inlineCode",{parentName:"p"},"/self-service/registration/api"),", ...):"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-shell-session"},'$ curl -s -X GET \\\n    -H "Accept: application/json"  \\\n    https://playground.projects.oryapis.com/api/kratos/public/self-service/login/api | jq\n\n{\n  "id": "9d17f37b-b60b-44f5-9812-4829a89810f7",\n  "type": "api",\n  "expires_at": "2021-07-09T11:26:04.019418543Z",\n  "issued_at": "2021-07-09T10:26:04.019418543Z",\n  "request_url": "http://playground.projects.oryapis.com/self-service/login/api",\n  "ui": {\n    "action": "https://playground.projects.oryapis.com/api/kratos/public/self-service/login?flow=9d17f37b-b60b-44f5-9812-4829a89810f7",\n    "method": "POST",\n    "nodes": [ /* ... */ ]\n  }\n}\n')),(0,i.kt)("p",null,'The initialization endpoint creates a flow object and stores it in the database.\nThe flow object has an ID and contains additional information about the flow\nsuch as the login methods (e.g. "username/password" and "Sign in with Google")\nand their form data.'),(0,i.kt)("h3",{id:"form-rendering-2"},"Form Rendering"),(0,i.kt)("p",null,"While the Browser flow redirects the client and uses cookies to protect against\nCSRF and session hijacking attacks, the API flow answers with a fresh flow\nformatted as ",(0,i.kt)("inlineCode",{parentName:"p"},"application/json")," right away. The client would then use that\ninformation to render the UI. In React Native, this may look like the following\nsnippet:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-tsx"},"import React, { useState, useEffect } from 'react'\nimport { Text, TextInput, View } from 'react-native'\nimport { Configuration, V0alpha2Api, LoginFlow } from '@ory/kratos-client'\n\nconst kratos = new V0alpha2Api(\n  new Configuration({ basePath: 'https://playground.projects.oryapis.com/api/kratos/public' })\n)\n\nexport default function Login () {\n  const [flow, setFlow] = useState<LoginFlow | undefined>(undefined)\n\n  useEffect(() => {\n    kratos.initializeSelfServiceLoginFlowWithoutBrowser().then(({ data: flow }) => {\n      setFlow(flow)\n    })\n  }, [])\n\n  if (!flow) {\n    return null\n  }\n\n  // This is a non-functional, code example. It is missing state management and so on.\n  // The idea is to show how initializing such a flow would look like in an API scenario.\n  return (\n    <View>\n      <Text>Login</Text>\n\n      {flow.ui.nodes.map((node) => {\n        switch (node.type) {\n          case 'input':\n            return <TextInput value={node.attributes.value} /* placeholder, name, ... *//>\n          default:\n            // ...\n        }\n      })}\n    </>\n  )\n}\n")),(0,i.kt)("p",null,"If needed, the client can re-request the flow using the same HTTP Request as the\nbrowser flow at the Public API endpoint:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-shell",metastring:"script",script:!0},'$ curl -s -X GET \\\n    -H "Accept: application/json" \\\n    "https://playground.projects.oryapis.com/api/kratos/public/self-service/login/flows?id=<the-flow-id>" | jq\n\n{\n  "id": "41ebf1e9-79f5-4b97-b643-04bfc405f8ad",\n  "type": "api",\n  # ...\n')),(0,i.kt)("h3",{id:"form-submission-and-payload-validation-2"},"Form Submission and Payload Validation"),(0,i.kt)("p",null,"The request is then completed by sending the form formatted as\n",(0,i.kt)("inlineCode",{parentName:"p"},"application/json")," to the action endpoint"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-shell"},'flow=$(curl -s -X GET -H "Accept: application/json" "https://playground.projects.oryapis.com/api/kratos/public/self-service/login/api")\nactionUrl=$(echo $flow | jq -r \'.ui.action\')\n\necho $actionUrl\n# https://playground.projects.oryapis.com/api/kratos/public/self-service/login?flow=6394ffa4-235f-4c1a-a200-e62f89862015\n\ncurl -s -X POST -H  "Accept: application/json" -H "Content-Type: application/json" \\\n    -d \'{"password_identifier": "i-do-not-exist@user.org", "password": "the-wrong-password", "method": "password"}\' \\\n    "$actionUrl" | jq\n')),(0,i.kt)("p",null,"which in this case fails with a 400 Bad Request error and the following payload:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json"},'{\n  "id": "6394ffa4-235f-4c1a-a200-e62f89862015",\n  "type": "api",\n  "expires_at": "2021-04-28T09:12:48.462374732Z",\n  "issued_at": "2021-04-28T09:02:48.462374732Z",\n  "request_url": "https://playground.projects.oryapis.com/api/kratos/public/self-service/login/api",\n  "ui": {\n    "action": "https://playground.projects.oryapis.com/api/kratos/public/self-service/login?flow=6394ffa4-235f-4c1a-a200-e62f89862015",\n    "method": "POST",\n    "messages": [\n      {\n        "id": 4000006,\n        "text": "The provided credentials are invalid, check for spelling mistakes in your password or username, email address, or phone number.",\n        "type": "error",\n        "context": {}\n      }\n    ],\n    "nodes": [\n      {\n        "type": "input",\n        "group": "default",\n        "attributes": {\n          "name": "csrf_token",\n          "type": "hidden",\n          "value": "",\n          "required": true,\n          "disabled": false\n        },\n        "messages": null,\n        "meta": {}\n      },\n      {\n        "type": "input",\n        "group": "password",\n        "attributes": {\n          "name": "password_identifier",\n          "type": "text",\n          "value": "i-do-not-exist@user.org",\n          "required": true,\n          "disabled": false\n        },\n        "messages": null,\n        "meta": {\n          "label": {\n            "id": 1070004,\n            "text": "ID",\n            "type": "info"\n          }\n        }\n      },\n      {\n        "type": "input",\n        "group": "password",\n        "attributes": {\n          "name": "password",\n          "type": "password",\n          "required": true,\n          "disabled": false\n        },\n        "messages": null,\n        "meta": {\n          "label": {\n            "id": 1070001,\n            "text": "Password",\n            "type": "info"\n          }\n        }\n      },\n      {\n        "type": "input",\n        "group": "password",\n        "attributes": {\n          "name": "method",\n          "type": "submit",\n          "value": "password",\n          "disabled": false\n        },\n        "messages": null,\n        "meta": {\n          "label": {\n            "id": 1010001,\n            "text": "Sign in",\n            "type": "info",\n            "context": {}\n          }\n        }\n      }\n    ]\n  },\n  "forced": false\n}\n')),(0,i.kt)("p",null,"On success, that endpoint would typically return a HTTP 200 Status OK response\nwith the success ",(0,i.kt)("inlineCode",{parentName:"p"},"application/json")," response payload in the body."))}w.isMDXComponent=!0},11748:function(e,n,t){var a={"./locale":89234,"./locale.js":89234};function o(e){var n=i(e);return t(n)}function i(e){if(!t.o(a,e)){var n=new Error("Cannot find module '"+e+"'");throw n.code="MODULE_NOT_FOUND",n}return a[e]}o.keys=function(){return Object.keys(a)},o.resolve=i,e.exports=o,o.id=11748},83300:function(e,n){"use strict";var t=function(){if("undefined"!=typeof self)return self;if("undefined"!=typeof window)return window;if(void 0!==t)return t;throw new Error("unable to locate global object")}();e.exports=n=t.fetch,t.fetch&&(n.default=t.fetch.bind(t)),n.Headers=t.Headers,n.Request=t.Request,n.Response=t.Response},89954:function(e,n){"use strict";n.Z='$ curl -s -H "Accept: application/json" \\\n    \'https://playground.projects.oryapis.com/api/kratos/public/self-service/login/flows?id=76cec270-1719-4c9e-b09a-4af8281d511e\' \\\n    | jq -r \'.ui.messages\'\n\n[\n  {\n    "id": 4000001,\n    "text": "Authentication failed because no id_token was returned. Please accept the \\"openid\\" permission and try again.",\n    "type": "error"\n  }\n]\n'},53009:function(e,n){"use strict";n.Z='$ curl -H "Accept: application/json" -s \\\n    \'https://playground.projects.oryapis.com/api/kratos/public/self-service/login/flows?id=a2d6e166-5153-42a6-8943-ef99eba8ab2e\' \\\n    | jq -r \'.ui.nodes[] | select(.attributes.name=="password_identifier")\'\n\n{\n  "type": "input",\n  "group": "password",\n  "attributes": {\n    "name": "password_identifier",\n    "type": "text",\n    "value": "",\n    "required": true,\n    "disabled": false\n  },\n  "messages": [\n    {\n      "id": 4000001,\n      "text": "length must be >= 1, but got 0",\n      "type": "error"\n    }\n  ],\n  "meta": {\n    "label": {\n      "id": 1070004,\n      "text": "ID",\n      "type": "info"\n    }\n  }\n}\n'},2745:function(e,n){"use strict";n.Z='$ curl -H "Accept: application/json" -s \\\n    \'https://playground.projects.oryapis.com/api/kratos/public/self-service/login/flows?id=a2d6e166-5153-42a6-8943-ef99eba8ab2e\' \\\n    | jq -r \'.ui.messages\'\n\n[\n  {\n    "id": 4000006,\n    "text": "The provided credentials are invalid, check for spelling mistakes in your password or username, email address, or phone number.",\n    "type": "error",\n    "context": {}\n  }\n]\n'},69885:function(e,n,t){"use strict";n.Z=t.p+"assets/images/browser-error-b6509d4065a4bfb5ab01e37a0389a65f.png"}}]);