text: (window.webpackJsonp=window.webpackJsonp||[]).push([[29],{169:function(e,t,n){"use strict";n.r(t),n.d(t,"frontMatter",(function(){return r})),n.d(t,"metadata",(function(){return s})),n.d(t,"rightToc",(function(){return c})),n.d(t,"default",(function(){return h}));var a=n(1),o=n(9),i=(n(0),n(224)),r={id:"oauth2",title:"OAuth 2.0 and OpenID Connect",sidebar_label:"OAuth 2.0"},s={id:"version-v1.4/oauth2",title:"OAuth 2.0 and OpenID Connect",description:"This section describes on a high level what OAuth 2.0 and OpenID Connect 1.0 are",source:"@site/versioned_docs/version-v1.4/oauth2.md",permalink:"/hydra/docs/v1.4/oauth2",editUrl:"https://github.com/ory/hydra/edit/master/docs/versioned_docs/version-v1.4/oauth2.md",version:"v1.4",lastUpdatedBy:"aeneasr",lastUpdatedAt:1587718887,sidebar_label:"OAuth 2.0",sidebar:"version-v1.4/docs",previous:{title:"Installation",permalink:"/hydra/docs/v1.4/install"},next:{title:"Hydra's User Login and Consent Flow",permalink:"/hydra/docs/v1.4/login-consent-flow"}},c=[{value:"OAuth 2.0",id:"oauth-20",children:[]},{value:"What is OpenID Connect 1.0?",id:"what-is-openid-connect-10",children:[]},{value:"OAuth 2.0 Scope != Permission",id:"oauth-20-scope--permission",children:[]}],l={rightToc:c};function h(e){var t=e.components,n=Object(o.a)(e,["components"]);return Object(i.b)("wrapper",Object(a.a)({},l,n,{components:t,mdxType:"MDXLayout"}),Object(i.b)("p",null,"This section describes on a high level what OAuth 2.0 and OpenID Connect 1.0 are\nfor and how they work."),Object(i.b)("h3",{id:"oauth-20"},"OAuth 2.0"),Object(i.b)("p",null,Object(i.b)("a",Object(a.a)({parentName:"p"},{href:"https://tools.ietf.org/html/rfc6749"}),"The OAuth 2.0 authorization framework")," is\nspecified in ",Object(i.b)("a",Object(a.a)({parentName:"p"},{href:"https://tools.ietf.org/html/rfc6749"}),"IETF RFC 6749"),". OAuth 2.0\nenables a third-party application to obtain limited access to resources on an\nHTTP server on behalf of the owner of those resources."),Object(i.b)("p",null,"Why is this important? Without OAuth 2.0, a resource owner who wants to share\nresources in their account with a third party would have to share their\ncredentials with that third party. As an example, let's say you (a resource\nowner) have some photos (resources) stored on a social network (the resource\nserver). Now you want to print them using a third-party printing service. Before\nOAuth 2.0 existed, you would have to enter your social network password into the\nprinting service so that it can access and print your photos. Sharing secret\npasswords with third parties is obviously very problematic."),Object(i.b)("p",null,"OAuth addresses this problem by introducing:"),Object(i.b)("ul",null,Object(i.b)("li",{parentName:"ul"},"the distinction between resource ownership and resource access for clients"),Object(i.b)("li",{parentName:"ul"},"the ability to define fine-grained access privileges (called OAuth scopes)\ninstead of full account access for third parties"),Object(i.b)("li",{parentName:"ul"},"an authorization layer and workflow that allows resource owners to grant\nparticular clients particular types of access to particular resources.")),Object(i.b)("p",null,"With OAuth, clients can request access to resources on a server, and the owner\nof these resources can grant the requested access together with dedicated\ncredentials. In our example, you could grant the printing service read-only\naccess to your photos (only your photos, not your friend list) on the social\nnetwork. These credentials come in the form of an access token -- a string\ndenoting a specific scope, lifetime, and other access attributes. The client\n(printing service) can use this access token to request the protected resources\n(your photos) from the resource server (the social network)."),Object(i.b)("h3",{id:"what-is-openid-connect-10"},"What is OpenID Connect 1.0?"),Object(i.b)("p",null,"OAuth 2.0 is a complex protocol for authorizing access to resources. If all you\nneed is authentication, OpenID Connect 1.0 enables clients to verify the\nidentity of the end user based on the authentication performed by an\nAuthorization Server and obtain basic profile information in an interoperable\nand REST-like manner."),Object(i.b)("p",null,"OpenID Connect allows clients of all types, including web and mobile, to receive\ninformation about authenticated sessions and end users. The specification is\nextensible, allowing participants to add encryption of identity data, discovery\nof OpenID Providers, and session management as needed."),Object(i.b)("p",null,"There are different work flows for OpenID Connect 1.0. We recommend checking out\nthe OpenID Connect sandbox at ",Object(i.b)("a",Object(a.a)({parentName:"p"},{href:"https://openidconnect.net/"}),"openidconnect.net"),"."),Object(i.b)("p",null,"A more detailed introduction of both OAuth 2.0 and OpenID Connect is available\nin the following video:"),Object(i.b)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/996OiexHze0",frameborder:"0",allowfullscreen:!0}),Object(i.b)("p",null,"More details about the various OAuth2 flows can be found in these articles:"),Object(i.b)("ul",null,Object(i.b)("li",{parentName:"ul"},Object(i.b)("a",Object(a.a)({parentName:"li"},{href:"https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2"}),"DigitalOcean: An Introduction to OAuth 2")),Object(i.b)("li",{parentName:"ul"},Object(i.b)("a",Object(a.a)({parentName:"li"},{href:"https://aaronparecki.com/2012/07/29/2/oauth2-simplified"}),"Aaron Parecki: OAuth2 Simplified")),Object(i.b)("li",{parentName:"ul"},Object(i.b)("a",Object(a.a)({parentName:"li"},{href:"https://zapier.com/learn/apis/chapter-5-authentication-part-2/"}),"Zapier: Chapter 5: Authentication, Part 2"))),Object(i.b)("ol",null,Object(i.b)("li",{parentName:"ol"},"ORY Hydra does not manage user accounts. Your application does that. Hydra\nexposes an OAuth 2.0 and OpenID Connect endpoint for the user accounts of\nyour application."),Object(i.b)("li",{parentName:"ol"},"OAuth Scopes are not access permissions to resources. They entitle an\nexternal application to act in the name of a user on a particular type of\nresource.")),Object(i.b)("h3",{id:"oauth-20-scope--permission"},"OAuth 2.0 Scope != Permission"),Object(i.b)("div",{className:"admonition admonition-info alert alert--info"},Object(i.b)("div",Object(a.a)({parentName:"div"},{className:"admonition-heading"}),Object(i.b)("h5",{parentName:"div"},Object(i.b)("span",Object(a.a)({parentName:"h5"},{className:"admonition-icon"}),Object(i.b)("svg",Object(a.a)({parentName:"span"},{xmlns:"http://www.w3.org/2000/svg",width:"14",height:"16",viewBox:"0 0 14 16"}),Object(i.b)("path",Object(a.a)({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"})))),"The OAuth2 Scope reflects a permission the user gave to the OAuth2")),Object(i.b)("div",Object(a.a)({parentName:"div"},{className:"admonition-content"}),Object(i.b)("p",{parentName:"div"},"Application, not a permission the system (e.g. API) gave to that OAuth2\napplication. Also, the OAuth2 Scope can not be changed without revoking the\ntoken. :::"),Object(i.b)("p",{parentName:"div"},"A second important concept is the OAuth 2.0 Scope. Many people confuse OAuth 2.0\nScope with internal Access Control like for example Role Based Access Control\n(RBAC) or Access Control Lists (ACL). Both concepts cover different aspects of\naccess control."),Object(i.b)("p",{parentName:"div"},"Internal access control (RBAC, ACL, etc) decides what a user can do in your\nsystem. An administrator might be allowed to modify everything. A regular user\nmight only be allowed to read their own messages."),Object(i.b)("p",{parentName:"div"},"OAuth 2.0 Scopes, on the other hand, describe what a user allowed an external\napplication (OAuth 2.0 client) to do on his/her behalf. For example, an access\ntoken might grant the external application to see a user's pictures, but not\nupload new pictures on his/her behalf (which we assume a user could do herself)."),Object(i.b)("p",{parentName:"div"},"In an extreme case, the user could lie and grant an external application OAuth\nscopes that he himself doesn't have permission to (\"read all classified\ndocuments\"). The OAuth Access Token with those scopes wouldn't help the external\napplication read those documents because it can only act in the name of the\nuser, and that user doesn't have these access privileges."),Object(i.b)("p",{parentName:"div"},"To recap, ORY Hydra's primary feature is implementing the OAuth 2.0 and OpenID\nConnect spec, as well as related specs by the IETF and OpenID Foundation."),Object(i.b)("p",{parentName:"div"},"The next sections explain how to connect your existing user management (user\nlogin, registration, logout, ...) with ORY Hydra in order to become an OAuth 2.0\nand OpenID Connect provider like Google, Dropbox, or Facebook."),Object(i.b)("p",{parentName:"div"},"Again, please be aware that you must know how OAuth 2.0 and OpenID Connect work.\nThis documentation will not teach you how these protocols work."),Object(i.b)("h3",Object(a.a)({parentName:"div"},{id:"terminology"}),"Terminology"),Object(i.b)("p",{parentName:"div"},"To read more natural, this guide uses simpler terminologies like ",Object(i.b)("em",{parentName:"p"},"user")," instead\nof ",Object(i.b)("em",{parentName:"p"},"resource owner"),". Here is a full list of terms."),Object(i.b)("ol",{parentName:"div"},Object(i.b)("li",{parentName:"ol"},"A ",Object(i.b)("strong",{parentName:"li"},"resource owner"),' is the user account who authorizes an external\napplication to access their account. This access is limited (scoped) to\nparticular actions (the granted "scopes" like read photos or write messages).\nThis guide refers to resource owners as ',Object(i.b)("em",{parentName:"li"},"users")," or ",Object(i.b)("em",{parentName:"li"},"end users"),"."),Object(i.b)("li",{parentName:"ol"},"The ",Object(i.b)("strong",{parentName:"li"},"OAuth 2.0 Authorization Server")," implements the OAuth 2.0 protocol (and\noptionally OpenID Connect). In our case, this is ",Object(i.b)("strong",{parentName:"li"},"ORY Hydra"),"."),Object(i.b)("li",{parentName:"ol"},"The ",Object(i.b)("strong",{parentName:"li"},"resource provider")," is the service that hosts (provides) the resources.\nThese resources (e.g. blog articles, printers, todo lists) are owned by a\nresource owner (user) mentioned above."),Object(i.b)("li",{parentName:"ol"},"The ",Object(i.b)("strong",{parentName:"li"},"OAuth 2.0 Client")," is the ",Object(i.b)("em",{parentName:"li"},"external application")," that wants to access a\nresource owner's resources (read a user's images). To do that, it asks the\nOAuth 2.0 Authorization Server for an access token in a resource owner's\nbehalf. The authorization server will ask the user if he/she \"is ok with\"\ngiving that external application e.g. write access to personal images."),Object(i.b)("li",{parentName:"ol"},"The ",Object(i.b)("strong",{parentName:"li"},"Identity Provider")," is a service that allows users to register\naccounts, log in, etc."),Object(i.b)("li",{parentName:"ol"},Object(i.b)("strong",{parentName:"li"},"User Agent")," is usually a browser."),Object(i.b)("li",{parentName:"ol"},Object(i.b)("strong",{parentName:"li"},"OpenID Connect")," is a protocol built on top of OAuth 2.0 for just\nauthentication (instead of authorizing access to resources).")),Object(i.b)("p",{parentName:"div"},"A typical OAuth 2.0 flow looks as follows:"),Object(i.b)("ol",{parentName:"div"},Object(i.b)("li",{parentName:"ol"},"A developer registers an OAuth 2.0 Client (external application) with the\nAuthorization Server (ORY Hydra) the intention to obtain information on\nbehalf of a user."),Object(i.b)("li",{parentName:"ol"},"The application UI asks the user to authorize the application to access\ninformation/data on his/her behalf."),Object(i.b)("li",{parentName:"ol"},"The user is redirected to the Authorization Server."),Object(i.b)("li",{parentName:"ol"},"The Authorization Server confirms the user's identity and asks the user to\ngrant the OAuth 2.0 Client certain permissions."),Object(i.b)("li",{parentName:"ol"},"The Authorization Server issues tokens that the OAuth 2.0 client uses to\naccess resources on the user's behalf.")),Object(i.b)("h2",Object(a.a)({parentName:"div"},{id:"authenticating-users-and-requesting-consent"}),"Authenticating Users and Requesting Consent"),Object(i.b)("p",{parentName:"div"},"As you already know by now, ORY Hydra does not come with any type of user\nmanagement (login, registration, ...). Instead, it relies on the so-called User\nLogin and Consent Flow. This flow describes a series of redirects where the\nuser's user agent is redirect to your Login Provider and, once the user is\nauthenticated, to the Consent Provider. The Login and Consent provider is\nimplemented by you in a programming language of your choice. You could write,\nfor example, a NodeJS app that handles HTTP requests to ",Object(i.b)("inlineCode",{parentName:"p"},"/login")," and ",Object(i.b)("inlineCode",{parentName:"p"},"/consent"),"\nand it would thus be your Login & Consent provider."),Object(i.b)("p",{parentName:"div"},"The flow itself works as follows:"),Object(i.b)("ol",{parentName:"div"},Object(i.b)("li",{parentName:"ol"},"The OAuth 2.0 Client initiates an Authorize Code, Hybrid, or Implicit flow.\nThe user's user agent is redirected to\n",Object(i.b)("inlineCode",{parentName:"li"},"http://hydra/oauth2/auth?client_id=...&..."),"."),Object(i.b)("li",{parentName:"ol"},'ORY Hydra, if unable to authenticate the user (meaning no session cookie\nexists), redirects the user\'s user agent to the Login Provider URL. The\napplication "sitting" at that URL is implemented by you and typically shows a\nlogin user interface ("Please enter your username and password"). The URL the\nuser is redirected to looks similar to\n',Object(i.b)("inlineCode",{parentName:"li"},"http://login-service/login?login_challenge=1234..."),"."),Object(i.b)("li",{parentName:"ol"},"The Login Provider, once the user has successfully logged in, tells ORY Hydra\nsome information about who the user is (e.g. the user's ID) and also that the\nlogin attempt was successful. This is done using a REST request which\nincludes another redirect URL along the lines of\n",Object(i.b)("inlineCode",{parentName:"li"},"http://hydra/oauth2/auth?client_id=...&...&login_verifier=4321"),"."),Object(i.b)("li",{parentName:"ol"},"The user's user agent follows the redirect and lands back at ORY Hydra. Next,\nORY Hydra redirects the user's user agent to the Consent Provider, hosted\nat - for example - ",Object(i.b)("inlineCode",{parentName:"li"},"http://consent-service/consent?consent_challenge=4567...")),Object(i.b)("li",{parentName:"ol"},'The Consent Provider shows a user interface which asks the user if he/she\nwould like to grant the OAuth 2.0 Client the requested permissions ("OAuth\n2.0 Scope"). You\'ve probably seen this screen around, which is usually\nsomething similar to: ',Object(i.b)("em",{parentName:"li"},'"Would you like to grant Facebook Image Backup access\nto all your private and public images?"'),"."),Object(i.b)("li",{parentName:"ol"},"The Consent Provider makes another REST request to ORY Hydra to let it know\nwhich permissions the user authorized, and if the user authorized the request\nat all. The user can usually choose to not grant an application any access to\nhis/her personal data. In the response of that REST request, a redirect URL\nis included along the lines of\n",Object(i.b)("inlineCode",{parentName:"li"},"http://hydra/oauth2/auth?client_id=...&...&consent_verifier=7654..."),"."),Object(i.b)("li",{parentName:"ol"},"The user's user agent follows that redirect."),Object(i.b)("li",{parentName:"ol"},"Now, the user has successfully authenticated and authorized the application.\nNext, ORY Hydra will run some checks and if everything works out, issue\naccess, refresh, and ID tokens.")),Object(i.b)("p",{parentName:"div"},"This flow allows you to take full control of the behaviour of your login system\n(e.g. 2FA, passwordless, ...) and consent screen. A well-documented reference\nimplementation for both the Login and Consent Provider is\n",Object(i.b)("a",Object(a.a)({parentName:"p"},{href:"https://github.com/ory/hydra-login-consent-node"}),"available on GitHub"),"."),Object(i.b)("h3",Object(a.a)({parentName:"div"},{id:"the-flow-from-a-users-point-of-view"}),"The flow from a user's point of view"),Object(i.b)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/txUmfORzu8Y",frameborder:"0",allowfullscreen:!0}),Object(i.b)("h3",Object(a.a)({parentName:"div"},{id:"the-flow-from-a-network-perspective"}),"The flow from a network perspective"),Object(i.b)("p",{parentName:"div"},Object(i.b)("a",Object(a.a)({parentName:"p"},{href:"https://mermaid-js.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoic2VxdWVuY2VEaWFncmFtXG4gICAgT0F1dGgyIENsaWVudC0-Pk9SWSBIeWRyYTogSW5pdGlhdGVzIE9BdXRoMiBBdXRob3JpemUgQ29kZSBvciBJbXBsaWNpdCBGbG93XG4gICAgT1JZIEh5ZHJhLS0-Pk9SWSBIeWRyYTogTm8gZW5kIHVzZXIgc2Vzc2lvbiBhdmFpbGFibGUgKG5vdCBhdXRoZW50aWNhdGVkKVxuICAgIE9SWSBIeWRyYS0-PkxvZ2luIFByb3ZpZGVyOiBSZWRpcmVjdHMgZW5kIHVzZXIgd2l0aCBsb2dpbiBjaGFsbGVuZ2VcbiAgICBMb2dpbiBQcm92aWRlci0tPk9SWSBIeWRyYTogRmV0Y2hlcyBsb2dpbiBpbmZvXG4gICAgTG9naW4gUHJvdmlkZXItLT4-TG9naW4gUHJvdmlkZXI6IEF1dGhlbnRpY2F0ZXMgdXNlciB3aXRoIGNyZWRlbnRpYWxzXG4gICAgTG9naW4gUHJvdmlkZXItLT5PUlkgSHlkcmE6IFRyYW5zbWl0cyBsb2dpbiBpbmZvIGFuZCByZWNlaXZlcyByZWRpcmVjdCB1cmwgd2l0aCBsb2dpbiB2ZXJpZmllclxuICAgIExvZ2luIFByb3ZpZGVyLT4-T1JZIEh5ZHJhOiBSZWRpcmVjdHMgZW5kIHVzZXIgdG8gcmVkaXJlY3QgdXJsIHdpdGggbG9naW4gdmVyaWZpZXJcbiAgICBPUlkgSHlkcmEtLT4-T1JZIEh5ZHJhOiBGaXJzdCB0aW1lIHRoYXQgY2xpZW50IGFza3MgdXNlciBmb3IgcGVybWlzc2lvbnNcbiAgICBPUlkgSHlkcmEtPj5Db25zZW50IFByb3ZpZGVyOiBSZWRpcmVjdHMgZW5kIHVzZXIgd2l0aCBjb25zZW50IGNoYWxsZW5nZVxuICAgIENvbnNlbnQgUHJvdmlkZXItLT5PUlkgSHlkcmE6IEZldGNoZXMgY29uc2VudCBpbmZvICh3aGljaCB1c2VyLCB3aGF0IGFwcCwgd2hhdCBzY29wZXMpXG4gICAgQ29uc2VudCBQcm92aWRlci0tPj5Db25zZW50IFByb3ZpZGVyOiBBc2tzIGZvciBlbmQgdXNlcidzIHBlcm1pc3Npb24gdG8gZ3JhbnQgYXBwbGljYXRpb24gYWNjZXNzXG4gICAgQ29uc2VudCBQcm92aWRlci0tPk9SWSBIeWRyYTogVHJhbnNtaXRzIGNvbnNlbnQgcmVzdWx0IGFuZCByZWNlaXZlcyByZWRpcmVjdCB1cmwgd2l0aCBjb25zZW50IHZlcmlmaWVyXG4gICAgQ29uc2VudCBQcm92aWRlci0-Pk9SWSBIeWRyYTogUmVkaXJlY3RzIHRvIHJlZGlyZWN0IHVybCB3aXRoIGNvbnNlbnQgdmVyaWZpZXJcbiAgICBPUlkgSHlkcmEtLT4-T1JZIEh5ZHJhOiBWZXJpZmllcyBncmFudFxuICAgIE9SWSBIeWRyYS0-Pk9BdXRoMiBDbGllbnQ6IFRyYW5zbWl0cyBhdXRob3JpemF0aW9uIGNvZGUvdG9rZW4iLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9fQ"}),Object(i.b)("img",Object(a.a)({parentName:"a"},{src:"https://mermaid.ink/img/eyJjb2RlIjoic2VxdWVuY2VEaWFncmFtXG4gICAgT0F1dGgyIENsaWVudC0-Pk9SWSBIeWRyYTogSW5pdGlhdGVzIE9BdXRoMiBBdXRob3JpemUgQ29kZSBvciBJbXBsaWNpdCBGbG93XG4gICAgT1JZIEh5ZHJhLS0-Pk9SWSBIeWRyYTogTm8gZW5kIHVzZXIgc2Vzc2lvbiBhdmFpbGFibGUgKG5vdCBhdXRoZW50aWNhdGVkKVxuICAgIE9SWSBIeWRyYS0-PkxvZ2luIFByb3ZpZGVyOiBSZWRpcmVjdHMgZW5kIHVzZXIgd2l0aCBsb2dpbiBjaGFsbGVuZ2VcbiAgICBMb2dpbiBQcm92aWRlci0tPk9SWSBIeWRyYTogRmV0Y2hlcyBsb2dpbiBpbmZvXG4gICAgTG9naW4gUHJvdmlkZXItLT4-TG9naW4gUHJvdmlkZXI6IEF1dGhlbnRpY2F0ZXMgdXNlciB3aXRoIGNyZWRlbnRpYWxzXG4gICAgTG9naW4gUHJvdmlkZXItLT5PUlkgSHlkcmE6IFRyYW5zbWl0cyBsb2dpbiBpbmZvIGFuZCByZWNlaXZlcyByZWRpcmVjdCB1cmwgd2l0aCBsb2dpbiB2ZXJpZmllclxuICAgIExvZ2luIFByb3ZpZGVyLT4-T1JZIEh5ZHJhOiBSZWRpcmVjdHMgZW5kIHVzZXIgdG8gcmVkaXJlY3QgdXJsIHdpdGggbG9naW4gdmVyaWZpZXJcbiAgICBPUlkgSHlkcmEtLT4-T1JZIEh5ZHJhOiBGaXJzdCB0aW1lIHRoYXQgY2xpZW50IGFza3MgdXNlciBmb3IgcGVybWlzc2lvbnNcbiAgICBPUlkgSHlkcmEtPj5Db25zZW50IFByb3ZpZGVyOiBSZWRpcmVjdHMgZW5kIHVzZXIgd2l0aCBjb25zZW50IGNoYWxsZW5nZVxuICAgIENvbnNlbnQgUHJvdmlkZXItLT5PUlkgSHlkcmE6IEZldGNoZXMgY29uc2VudCBpbmZvICh3aGljaCB1c2VyLCB3aGF0IGFwcCwgd2hhdCBzY29wZXMpXG4gICAgQ29uc2VudCBQcm92aWRlci0tPj5Db25zZW50IFByb3ZpZGVyOiBBc2tzIGZvciBlbmQgdXNlcidzIHBlcm1pc3Npb24gdG8gZ3JhbnQgYXBwbGljYXRpb24gYWNjZXNzXG4gICAgQ29uc2VudCBQcm92aWRlci0tPk9SWSBIeWRyYTogVHJhbnNtaXRzIGNvbnNlbnQgcmVzdWx0IGFuZCByZWNlaXZlcyByZWRpcmVjdCB1cmwgd2l0aCBjb25zZW50IHZlcmlmaWVyXG4gICAgQ29uc2VudCBQcm92aWRlci0-Pk9SWSBIeWRyYTogUmVkaXJlY3RzIHRvIHJlZGlyZWN0IHVybCB3aXRoIGNvbnNlbnQgdmVyaWZpZXJcbiAgICBPUlkgSHlkcmEtLT4-T1JZIEh5ZHJhOiBWZXJpZmllcyBncmFudFxuICAgIE9SWSBIeWRyYS0-Pk9BdXRoMiBDbGllbnQ6IFRyYW5zbWl0cyBhdXRob3JpemF0aW9uIGNvZGUvdG9rZW4iLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9fQ",alt:"ORY Hydra Login and Consent Flow"})))),Object(i.b)("h3",Object(a.a)({parentName:"div"},{id:"implementing-a-login--consent-provider"}),"Implementing a Login & Consent Provider"),Object(i.b)("p",{parentName:"div"},"You should now have a high-level idea of how the login and consent providers\nwork. Let's get into the details of it."),Object(i.b)("h4",Object(a.a)({parentName:"div"},{id:"oauth-20-authorize-code-flow"}),"OAuth 2.0 Authorize Code Flow"),Object(i.b)("p",{parentName:"div"},"Before anything happens, the OAuth 2.0 Authorize Code Flow is initiated by an\nOAuth 2.0 Client. This usually works by generating a URL in the form of\n",Object(i.b)("inlineCode",{parentName:"p"},"https://hydra/oauth2/auth?client_id=1234&scope=foo+bar&response_type=code&..."),".\nThen, the OAuth 2.0 Client points the end user's user agent to that URL."),Object(i.b)("p",{parentName:"div"},"Next, the user agent (browser) opens that URL."),Object(i.b)("h4",Object(a.a)({parentName:"div"},{id:"user-login"}),"User Login"),Object(i.b)("p",{parentName:"div"},"As the user agent hits the URL, ORY Hydra checks if a session cookie is set\ncontaining information about a previously successful login. Additionally,\nparameters such as ",Object(i.b)("inlineCode",{parentName:"p"},"id_token_hint"),", ",Object(i.b)("inlineCode",{parentName:"p"},"prompt"),", and ",Object(i.b)("inlineCode",{parentName:"p"},"max_age")," are evaluated and\nprocessed."),Object(i.b)("p",{parentName:"div"},"Next, the user will be redirect to the Login Provider which was set using the\n",Object(i.b)("inlineCode",{parentName:"p"},"URLS_LOGIN")," environment variable. For example, the user is redirected to\n",Object(i.b)("inlineCode",{parentName:"p"},"https://login-provider/login?login_challenge=1234")," if\n",Object(i.b)("inlineCode",{parentName:"p"},"URLS_LOGIN=https://login-provider/login"),". This redirection happens ",Object(i.b)("em",{parentName:"p"},"always")," and\nregardless of whether the user has a valid login session or if the user needs to\nauthenticate."),Object(i.b)("p",{parentName:"div"},"The service which handles requests to ",Object(i.b)("inlineCode",{parentName:"p"},"https://login-provider/login")," must first\nfetch information on the authentication request using a REST API call. Please be\naware that for reasons of brevity, the following code snippets are pseudo-code.\nFor a fully working example, check out our reference\n",Object(i.b)("a",Object(a.a)({parentName:"p"},{href:"https://github.com/ory/hydra-login-consent-node"}),"User Login & Consent Provider implementation"),"."),Object(i.b)("p",{parentName:"div"},"The endpoint handler at ",Object(i.b)("inlineCode",{parentName:"p"},"/login")," ",Object(i.b)("strong",{parentName:"p"},"must not remember previous sessions"),". This\ntask is solved by ORY Hydra. If the REST API call tells you to show the login\nui, you ",Object(i.b)("strong",{parentName:"p"},"must show it"),". If the REST API tells you to not show the login ui,\n",Object(i.b)("strong",{parentName:"p"},"you must not show it"),". Again, ",Object(i.b)("strong",{parentName:"p"},"do not implement any type of session here"),"."),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{}),"// This is node-js pseudo code and will not work if you copy it 1:1\n\nrouter.get('/login', function (req, res, next) {\n    challenge = req.url.query.login_challenge;\n\n    fetch('https://hydra/oauth2/auth/requests/login?' + querystring.stringify({ login_challenge: challenge })).\n        then(function (response) {\n            return response.json()\n        }).\n        then(function (response) {\n            // ...\n        })\n})\n")),Object(i.b)("p",{parentName:"div"},"The server response is a JSON object with the following keys:"),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{}),'{\n    // Skip, if true, let\'s us know that ORY Hydra has successfully authenticated the user and we should not show any UI\n    "skip": true|false,\n\n    // The user-id of the already authenticated user - only set if skip is true\n    "subject": "user-id",\n\n    // The OAuth 2.0 client that initiated the request\n    "client": {"id": "...", ...},\n\n    // The initial OAuth 2.0 request url\n    "request_url": "https://hydra/oauth2/auth?client_id=1234&scope=foo+bar&response_type=code&...",\n\n    // The OAuth 2.0 Scope requested by the client,\n    "requested_scope": ["foo", "bar"],\n\n    // Information on the OpenID Connect request - only required to process if your UI should support these values.\n    "oidc_context": {"ui_locales": [...], ...},\n\n    // Context is an optional object which can hold arbitrary data. The data will be made available when fetching the\n    // consent request under the "context" field. This is useful in scenarios where login and consent endpoints share\n    // data.\n    "context": {...}\n}\n')),Object(i.b)("p",{parentName:"div"},"For a full documentation on all available keys, please head over to the\n",Object(i.b)("a",Object(a.a)({parentName:"p"},{href:"https://www.ory.sh/docs/api/hydra/"}),"API documentation")," (make sure to select the\nright API version)."),Object(i.b)("p",{parentName:"div"},"Depending of whether or not ",Object(i.b)("inlineCode",{parentName:"p"},"skip")," is true, you will prompt the user to log in\nby showing him/her a username/password form, or by using some other proof of\nidentity."),Object(i.b)("p",{parentName:"div"},"If ",Object(i.b)("inlineCode",{parentName:"p"},"skip")," is true, you ",Object(i.b)("strong",{parentName:"p"},"should not")," show a user interface but accept the login\nrequest directly by making a REST call. You can use this step to update some\ninternal count of how often a user logged in, or do some other custom business\nlogic. But again, do not show the user interface."),Object(i.b)("p",{parentName:"div"},"To accept the login request, do something along the lines of:"),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{}),"// This is node-js pseudo code and will not work if you copy it 1:1\n\nconst body = {\n    // This is the user ID of the user that authenticated. If `skip` is true, this must be the `subject`\n    // value from the `fetch('https://hydra/oauth2/auth/requests/login?' + querystring.stringify({ login_challenge: challenge }))` response:\n    //\n    // subject = response.subject\n    //\n    // Otherwise, this can be a value of your choosing:\n    subject: \"...\",\n\n    // If remember is set to true, then the authentication session will be persisted in the user's browser by ORY Hydra. This will set the `skip` flag to true in future requests that are coming from this user. This value has no effect if `skip` was true.\n    remember: true|false,\n\n    // The time (in seconds) that the cookie should be valid for. Only has an effect if `remember` is true.\n    remember_for: 3600,\n\n    // This value is specified by OpenID connect and optional - it tells OpenID Connect which level of authentication the user performed - for example 2FA or using some biometric data. The concrete values are up to you here.\n    acr: \"..\"\n}\n\nfetch('https://hydra/oauth2/auth/requests/login/accept?' + querystring.stringify({ login_challenge: challenge }), {\n    method: 'PUT',\n    body: JSON.stringify(body),\n    headers: { 'Content-Type': 'application/json' }\n}).\n    then(function (response) {\n        return response.json()\n    }).\n    then(function (response) {\n        // The response will contain a `redirect_to` key which contains the URL where the user's user agent must be redirected to next.\n        res.redirect(response.redirect_to);\n    })\n")),Object(i.b)("p",{parentName:"div"},"You may also choose to deny the login request. This is possible regardless of\nthe ",Object(i.b)("inlineCode",{parentName:"p"},"skip")," value."),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{}),"// This is node-js pseudo code and will not work if you copy it 1:1\n\nconst body = {\n    error: \"...\", // This is an error ID like `login_required` or `invalid_request`\n    error_description: \"...\" // This is a more detailed description of the error\n}\n\nfetch('https://hydra/oauth2/auth/requests/login/reject?' + querystring.stringify({ login_challenge: challenge }), {\n    method: 'PUT',\n    body: JSON.stringify(body),\n    headers: { 'Content-Type': 'application/json' }\n}).\n    then(function (response) {\n        return response.json()\n    }).\n    then(function (response) {\n        // The response will contain a `redirect_to` key which contains the URL where the user's user agent must be redirected to next.\n        res.redirect(response.redirect_to);\n    })\n")),Object(i.b)("h4",Object(a.a)({parentName:"div"},{id:"user-consent"}),"User Consent"),Object(i.b)("p",{parentName:"div"},"Now that we know who the user is, we must ask the user if he/she wants to grant\nthe requested permissions to the OAuth 2.0 Client. To do so, we check if the\nuser has previously granted that exact OAuth 2.0 Client the requested\npermissions. If the user has never granted any permissions to the client, or the\nclient requires new permissions not previously granted, the user must visually\nconfirm the request."),Object(i.b)("p",{parentName:"div"},"This works very similar to the User Login Flow. First, the user will be\nredirected to the Consent Provider which was set using the ",Object(i.b)("inlineCode",{parentName:"p"},"URLS_CONSENT"),"\nenvironment variable. For example, the user is redirected to\n",Object(i.b)("inlineCode",{parentName:"p"},"https://consent-provider/consent?consent_challenge=1234")," if\n",Object(i.b)("inlineCode",{parentName:"p"},"URLS_CONSENT=https://consent-provider/consent"),". This redirection happens\n",Object(i.b)("em",{parentName:"p"},"always")," and regardless of whether the user has a valid login session or if the\nuser needs to authorize the application or not."),Object(i.b)("p",{parentName:"div"},"The service which handles requests to ",Object(i.b)("inlineCode",{parentName:"p"},"https://consent-provider/consent")," must\nfirst fetch information on the consent request using a REST API call. Please be\naware that for reasons of brevity, the following code snippets are pseudo-code.\nFor a fully working example, check out our reference\n",Object(i.b)("a",Object(a.a)({parentName:"p"},{href:"https://github.com/ory/hydra-login-consent-node"}),"User Login, Logout & Consent Provider implementation"),"."),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{}),"// This is node-js pseudo code and will not work if you copy it 1:1\n\nchallenge = req.url.query.consent_challenge;\n\nfetch('https://hydra/oauth2/auth/requests/consent?' + querystring.stringify({ consent_challenge: challenge })).\n    then(function (response) {\n        return response.json()\n    }).\n    then(function (response) {\n        // ...\n    })\n")),Object(i.b)("p",{parentName:"div"},"The server response is a JSON object with the following keys:"),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{}),'{\n    // Skip, if true, let\'s us know that the client has previously been granted the requested permissions (scope) by the end-user\n    "skip": true|false,\n\n    // The user-id of the user that will grant (or deny) the request\n    "subject": "user-id",\n\n    // The OAuth 2.0 client that initiated the request\n    "client": {"id": "...", ...},\n\n    // The initial OAuth 2.0 request url\n    "request_url": "https://hydra/oauth2/auth?client_id=1234&scope=foo+bar&response_type=code&...",\n\n    // The OAuth 2.0 Scope requested by the client.\n    "requested_scope": ["foo", "bar"],\n\n    // Contains the access token audience as requested by the OAuth 2.0 Client.\n    requested_access_token_audience: ["foo", "bar"]\n\n    // Information on the OpenID Connect request - only required to process if your UI should support these values.\n    "oidc_context": {"ui_locales": [...], ...},\n\n    // Contains arbitrary information set by the login endpoint or is empty if not set.\n    "context": {...}\n}\n')),Object(i.b)("p",{parentName:"div"},"If skip is true, you should not show any user interface to the user. Instead,\nyou should accept (or deny) the consent request. Typically, you will accept the\nrequest unless you have a very good reason to deny it (e.g. the OAuth 2.0 Client\nis banned)."),Object(i.b)("p",{parentName:"div"},"If skip is false and you show the consent screen, you should use the\n",Object(i.b)("inlineCode",{parentName:"p"},"requested_scope")," array to display a list of permissions which the user must\ngrant (e.g. using a checkbox). Some people choose to always skip this step if\nthe OAuth 2.0 Client is a first-party client - meaning that the client is used\nby you or your developers in an internal application."),Object(i.b)("p",{parentName:"div"},"Assuming the user accepts the consent request, the code looks very familiar to\nthe User Login Flow."),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{}),"// This is node-js pseudo code and will not work if you copy it 1:1\n\nconst body = {\n    // A list of permissions the user granted to the OAuth 2.0 Client. This can be fewer permissions that initially requested, but are rarely more or other permissions than requested.\n    grant_scope: [\"foo\", \"bar\"],\n\n    // Sets the audience the user authorized the client to use. Should be a subset of `requested_access_token_audience`.\n    grant_access_token_audience: [\"foo\", \"bar\"],\n\n    // If remember is set to true, then the consent response will be remembered for future requests. This will set the `skip` flag to true in future requests that are coming from this user for the granted permissions and that particular client. This value has no effect if `skip` was true.\n    remember: true|false,\n\n    // The time (in seconds) that the cookie should be valid for. Only has an effect if `remember` is true.\n    remember_for: 3600,\n\n    // The session allows you to set additional data in the access and ID tokens.\n    session: {\n        // Sets session data for the access and refresh token, as well as any future tokens issued by the\n        // refresh grant. Keep in mind that this data will be available to anyone performing OAuth 2.0 Challenge Introspection.\n        // If only your services can perform OAuth 2.0 Challenge Introspection, this is usually fine. But if third parties\n        // can access that endpoint as well, sensitive data from the session might be exposed to them. Use with care!\n        access_token: { ... },\n\n        // Sets session data for the OpenID Connect ID token. Keep in mind that the session'id payloads are readable\n        // by anyone that has access to the ID Challenge. Use with care! Any information added here will be mirrored at\n        // the `/userinfo` endpoint.\n        id_token: { ... },\n    }\n}\n\nfetch('https://hydra/oauth2/auth/requests/consent/accept?' + querystring.stringify({ consent_challenge: challenge }), {\n    method: 'PUT',\n    body: JSON.stringify(body),\n    headers: { 'Content-Type': 'application/json' }\n}).\n    then(function (response) {\n        return response.json()\n    }).\n    then(function (response) {\n        // The response will contain a `redirect_to` key which contains the URL where the user's user agent must be redirected to next.\n        res.redirect(response.redirect_to);\n    })\n")),Object(i.b)("p",{parentName:"div"},"You may also choose to deny the consent request. This is possible regardless of\nthe ",Object(i.b)("inlineCode",{parentName:"p"},"skip")," value."),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{}),"// This is node-js pseudo code and will not work if you copy it 1:1\n\nconst body = {\n    // This is an error ID like `consent_required` or `invalid_request`\n    error: \"...\",\n\n    // This is a more detailed description of the error\n    error_description: \"...\"\n}\n\nfetch('https://hydra/oauth2/auth/requests/consent/reject?' + querystring.stringify({ consent_challenge: challenge }), {\n    method: 'PUT',\n    body: JSON.stringify(body),\n    headers: { 'Content-Type': 'application/json' }\n}).\n    then(function (response) {\n        return response.json()\n    }).\n    then(function (response) {\n        // The response will contain a `redirect_to` key which contains the URL where the user's user agent must be redirected to next.\n        res.redirect(response.redirect_to);\n    })\n")),Object(i.b)("p",{parentName:"div"},"Once the user agent is redirected back, the OAuth 2.0 flow will be finalized."),Object(i.b)("h3",Object(a.a)({parentName:"div"},{id:"logout"}),"Logout"),Object(i.b)("p",{parentName:"div"},"ORY Hydra supports:"),Object(i.b)("ul",{parentName:"div"},Object(i.b)("li",{parentName:"ul"},Object(i.b)("a",Object(a.a)({parentName:"li"},{href:"https://openid.net/specs/openid-connect-frontchannel-1_0.html"}),"OpenID Connect Front-Channel Logout 1.0")),Object(i.b)("li",{parentName:"ul"},Object(i.b)("a",Object(a.a)({parentName:"li"},{href:"https://openid.net/specs/openid-connect-backchannel-1_0.html"}),"OpenID Connect Back-Channel Logout 1.0"))),Object(i.b)("p",{parentName:"div"},"A log out request may be initiated by a RP (Relying Party, alias for OAuth 2.0\nClient) or by calling the logout endpoint without any parameters. In both cases,\nthe high level flow looks as follows:"),Object(i.b)("ol",{parentName:"div"},Object(i.b)("li",{parentName:"ol"},"A user-agent (browser) requests the logout endpoint\n(",Object(i.b)("inlineCode",{parentName:"li"},"/oauth2/sessions/logout"),"). If the request is done on behalf of a RP:")),Object(i.b)("ul",{parentName:"div"},Object(i.b)("li",{parentName:"ul"},"The URL query MUST contain an ID Token issued by ORY Hydra as the\n",Object(i.b)("inlineCode",{parentName:"li"},"id_token_hint"),": ",Object(i.b)("inlineCode",{parentName:"li"},"/oauth2/sessions/logout?id_token_hint=...")),Object(i.b)("li",{parentName:"ul"},"The URL query MAY contain key ",Object(i.b)("inlineCode",{parentName:"li"},"post_logout_redirect_uri")," indicating where the\nuser agent should be redirected after the logout completed successfully. Each\nOAuth 2.0 Client can whitelist a list of URIs that can be used as the value\nusing the ",Object(i.b)("inlineCode",{parentName:"li"},"post_logout_redirect_uris")," metadata field:\n",Object(i.b)("inlineCode",{parentName:"li"},"/oauth2/sessions/logout?id_token_hint=...&post_logout_redirect_uri=https://i-must-be-whitelisted/")),Object(i.b)("li",{parentName:"ul"},"If ",Object(i.b)("inlineCode",{parentName:"li"},"post_logout_redirect_uri")," is set, the URL query SHOULD contain a ",Object(i.b)("inlineCode",{parentName:"li"},"state"),"\nvalue. On successful redirection, this state value will be appended to the\n",Object(i.b)("inlineCode",{parentName:"li"},"post_logout_redirect_uri"),". The functionality is equal to the ",Object(i.b)("inlineCode",{parentName:"li"},"state"),"\nparameter when performing OAuth2 flows.")),Object(i.b)("ol",Object(a.a)({parentName:"div"},{start:2}),Object(i.b)("li",{parentName:"ol"},"The user-agent is redirected to the logout provider URL (configuration item\n",Object(i.b)("inlineCode",{parentName:"li"},"urls.logout"),") and contains a challenge:\n",Object(i.b)("inlineCode",{parentName:"li"},"https://my-logout-provider/logout?challenge=...")),Object(i.b)("li",{parentName:"ol"},"The logout provider uses the ",Object(i.b)("inlineCode",{parentName:"li"},"challenge")," query parameter to fetch metadata\nabout the request. The logout provider may choose to show a UI where the user\nhas to accept the logout request. Alternatively, the logout provider MAY\nchoose to silently accept the logout request."),Object(i.b)("li",{parentName:"ol"},"To accept the logout request, the logout provider makes a ",Object(i.b)("inlineCode",{parentName:"li"},"PUT")," call to\n",Object(i.b)("inlineCode",{parentName:"li"},"/oauth2/auth/requests/logout/accept?challenge=..."),". No request body is\nrequired."),Object(i.b)("li",{parentName:"ol"},"The response contains a ",Object(i.b)("inlineCode",{parentName:"li"},"redirect_to")," value where the logout provider\nredirects the user back to."),Object(i.b)("li",{parentName:"ol"},"ORY Hydra performs OpenID Connect Front- and Back-Channel logout."),Object(i.b)("li",{parentName:"ol"},"The user agent is being redirected to a specified redirect URL. This may\neither be the default redirect URL set by ",Object(i.b)("inlineCode",{parentName:"li"},"urls.post_logout_redirect")," or to\nthe value specified by query parameter ",Object(i.b)("inlineCode",{parentName:"li"},"post_logout_redirect_uri"),".")),Object(i.b)("p",{parentName:"div"},Object(i.b)("a",Object(a.a)({parentName:"p"},{href:"https://mermaid-js.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoic2VxdWVuY2VEaWFncmFtXG4gICAgVXNlciBBZ2VudC0-Pk9SWSBIeWRyYTogQ2FsbHMgbG9nb3V0IGVuZHBvaW50XG4gICAgT1JZIEh5ZHJhLS0-Pk9SWSBIeWRyYTogVmFsaWRhdGVzIGxvZ291dCBlbmRwb2ludFxuICAgIE9SWSBIeWRyYS0-PkxvZ291dCBQcm92aWRlcjogUmVkaXJlY3RzIGVuZCB1c2VyIHdpdGggbG9nb3V0IGNoYWxsZW5nZVxuICAgIExvZ291dCBQcm92aWRlci0tPk9SWSBIeWRyYTogRmV0Y2hlcyBsb2dvdXQgcmVxdWVzdCBpbmZvXG4gICAgTG9nb3V0IFByb3ZpZGVyLS0-PkxvZ291dCBQcm92aWRlcjogQWNxdWlyZXMgdXNlciBjb25zZW50IGZvciBsb2dvdXQgKG9wdGlvbmFsKVxuICAgIExvZ291dCBQcm92aWRlci0tPk9SWSBIeWRyYTogSW5mb3JtcyB0aGF0IGxvZ291dCByZXF1ZXN0IGlzIGdyYW50ZWRcbiAgICBMb2dvdXQgUHJvdmlkZXItPj5PUlkgSHlkcmE6IFJlZGlyZWN0cyBlbmQgdXNlciB0byByZWRpcmVjdCB1cmwgd2l0aCBsb2dvdXQgY2hhbGxlbmdlXG4gICAgT1JZIEh5ZHJhLS0-Pk9SWSBIeWRyYTogUGVyZm9ybXMgbG9nb3V0IHJvdXRpbmVzXG4gICAgT1JZIEh5ZHJhLS0-VXNlciBBZ2VudDogUmVkaXJlY3RzIHRvIHNwZWNpZmllZCByZWRpcmVjdCB1cmwiLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9fQ"}),Object(i.b)("img",Object(a.a)({parentName:"a"},{src:"https://mermaid.ink/img/eyJjb2RlIjoic2VxdWVuY2VEaWFncmFtXG4gICAgVXNlciBBZ2VudC0-Pk9SWSBIeWRyYTogQ2FsbHMgbG9nb3V0IGVuZHBvaW50XG4gICAgT1JZIEh5ZHJhLS0-Pk9SWSBIeWRyYTogVmFsaWRhdGVzIGxvZ291dCBlbmRwb2ludFxuICAgIE9SWSBIeWRyYS0-PkxvZ291dCBQcm92aWRlcjogUmVkaXJlY3RzIGVuZCB1c2VyIHdpdGggbG9nb3V0IGNoYWxsZW5nZVxuICAgIExvZ291dCBQcm92aWRlci0tPk9SWSBIeWRyYTogRmV0Y2hlcyBsb2dvdXQgcmVxdWVzdCBpbmZvXG4gICAgTG9nb3V0IFByb3ZpZGVyLS0-PkxvZ291dCBQcm92aWRlcjogQWNxdWlyZXMgdXNlciBjb25zZW50IGZvciBsb2dvdXQgKG9wdGlvbmFsKVxuICAgIExvZ291dCBQcm92aWRlci0tPk9SWSBIeWRyYTogSW5mb3JtcyB0aGF0IGxvZ291dCByZXF1ZXN0IGlzIGdyYW50ZWRcbiAgICBMb2dvdXQgUHJvdmlkZXItPj5PUlkgSHlkcmE6IFJlZGlyZWN0cyBlbmQgdXNlciB0byByZWRpcmVjdCB1cmwgd2l0aCBsb2dvdXQgY2hhbGxlbmdlXG4gICAgT1JZIEh5ZHJhLS0-Pk9SWSBIeWRyYTogUGVyZm9ybXMgbG9nb3V0IHJvdXRpbmVzXG4gICAgT1JZIEh5ZHJhLS0-VXNlciBBZ2VudDogUmVkaXJlY3RzIHRvIHNwZWNpZmllZCByZWRpcmVjdCB1cmwiLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9fQ",alt:"ORY Hydra Lgoout flow"})))),Object(i.b)("p",{parentName:"div"},"This endpoint does not remove any Access/Refresh Tokens."),Object(i.b)("h4",Object(a.a)({parentName:"div"},{id:"logout-provider-example-nodejs-pseudo-code"}),"Logout Provider Example (NodeJS Pseudo-code)"),Object(i.b)("p",{parentName:"div"},"Following step 1 from the flow above, the user-agent is redirected to the logout\nprovider (e.g. ",Object(i.b)("inlineCode",{parentName:"p"},"https://my-logout-provider/logout?challenge=..."),"). Next, the\nlogout provider fetches information about the logout request:"),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{className:"language-node"}),"// This is node-js pseudo code and will not work if you copy it 1:1\n\nchallenge = req.url.query.logout_challenge;\n\nfetch(\n  'https://hydra/oauth2/auth/requests/logout?' +\n    querystring.stringify({ logout_challenge: challenge })\n)\n  .then(function (response) {\n    return response.json();\n  })\n  .then(function (response) {\n    // ...\n  });\n")),Object(i.b)("p",{parentName:"div"},"The server response is a JSON object with the following keys:"),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{}),'{\n    // The user for whom the logout was request.\n    "subject": "user-id",\n\n    // The login session ID that was requested to log out.\n    "sid": "abc..",\n\n    // The original request URL.\n    "request_url": "https://hydra/oauth2/sessions/logout?id_token_hint=...",\n\n    // True if the request was initiated by a Relying Party (RP) / OAuth 2.0 Client. False otherwise.\n    "rp_initiated": true|false\n}\n')),Object(i.b)("p",{parentName:"div"},"Next, the logout provider should decide if the end-user should perform a UI\naction such as confirming the logout request. It is RECOMMENDED to request\nlogout confirmation from the end-user when ",Object(i.b)("inlineCode",{parentName:"p"},"rp_initiated")," is set to true."),Object(i.b)("p",{parentName:"div"},"When the logout provider decides to accept the logout request, the flow is\ncompleted as follows:"),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{className:"language-node"}),"fetch(\n  'https://hydra/oauth2/auth/requests/logout/accept?' +\n    querystring.stringify({ logout_challenge: challenge }),\n  {\n    method: 'PUT',\n  }\n)\n  .then(function (response) {\n    return response.json();\n  })\n  .then(function (response) {\n    // The response will contain a `redirect_to` key which contains the URL where the user's user agent must be redirected to next.\n    res.redirect(response.redirect_to);\n  });\n")),Object(i.b)("p",{parentName:"div"},"You can also reject a logout request (e.g. if the user chose to not log out):"),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{className:"language-node"}),"fetch(\n  'https://hydra/oauth2/auth/requests/logout/reject?' +\n    querystring.stringify({ logout_challenge: challenge }),\n  {\n    method: 'PUT',\n  }\n).then(function (response) {\n  // Now you can do whatever you want - redirect the user back to your home page or whatever comes to mind.\n});\n")),Object(i.b)("p",{parentName:"div"},"If the logout request was granted and the user agent redirected back to ORY\nHydra, all OpenID Connect Front-/Back-channel logout flows (if set) will be\nperformed and the user will be redirect back to his/her final destination."),Object(i.b)("h4",Object(a.a)({parentName:"div"},{id:"openid-connect-front-channel-logout-10"}),Object(i.b)("a",Object(a.a)({parentName:"h4"},{href:"https://openid.net/specs/openid-connect-frontchannel-1_0.html"}),"OpenID Connect Front-Channel Logout 1.0")),Object(i.b)("p",{parentName:"div"},"In summary\n(",Object(i.b)("a",Object(a.a)({parentName:"p"},{href:"https://openid.net/specs/openid-connect-frontchannel-1_0.html"}),"read the spec"),")\nthis feature allows an OAuth 2.0 Client to register fields\n",Object(i.b)("inlineCode",{parentName:"p"},"frontchannel_logout_uri")," and ",Object(i.b)("inlineCode",{parentName:"p"},"frontchannel_logout_session_required"),"."),Object(i.b)("p",{parentName:"div"},"If ",Object(i.b)("inlineCode",{parentName:"p"},"frontchannel_logout_uri")," is set to a valid URL (the host, port, path must\nall match those of one of the Redirect URIs), ORY Hydra will redirect the\nuser-agent (typically browser) to that URL after a logout occurred. This allows\nthe OAuth 2.0 Client Application to log out the end-user in its own system as\nwell - for example by deleting a Cookie or otherwise invalidating the user\nsession."),Object(i.b)("p",{parentName:"div"},"ORY Hydra always appends query parameters values ",Object(i.b)("inlineCode",{parentName:"p"},"iss")," and ",Object(i.b)("inlineCode",{parentName:"p"},"sid")," to the\nFront-Channel Logout URI, for example:"),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{}),"https://rp.example.org/frontchannel_logout\n  ?iss=https://server.example.com\n  &sid=08a5019c-17e1-4977-8f42-65a12843ea02\n")),Object(i.b)("p",{parentName:"div"},"Each OpenID Connect ID Token is issued with a ",Object(i.b)("inlineCode",{parentName:"p"},"sid")," claim that will match the\n",Object(i.b)("inlineCode",{parentName:"p"},"sid")," value from the Front-Channel Logout URI."),Object(i.b)("p",{parentName:"div"},"ORY Hydra will automatically execute the required HTTP Redirects to make this\nwork. No extra work is required."),Object(i.b)("h4",Object(a.a)({parentName:"div"},{id:"openid-connect-back-channel-logout-10"}),Object(i.b)("a",Object(a.a)({parentName:"h4"},{href:"https://openid.net/specs/openid-connect-backchannel-1_0.html"}),"OpenID Connect Back-Channel Logout 1.0")),Object(i.b)("p",{parentName:"div"},"In summary\n(",Object(i.b)("a",Object(a.a)({parentName:"p"},{href:"https://openid.net/specs/openid-connect-backchannel-1_0.html"}),"read the spec"),")\nthis feature allows an OAuth 2.0 Client to register fields\n",Object(i.b)("inlineCode",{parentName:"p"},"backchannel_logout_uri")," and ",Object(i.b)("inlineCode",{parentName:"p"},"backchannel_logout_session_required"),"."),Object(i.b)("p",{parentName:"div"},"If ",Object(i.b)("inlineCode",{parentName:"p"},"backchannel_logout_uri")," is set to a valid URL, a HTTP Post request with\nContent-Type ",Object(i.b)("inlineCode",{parentName:"p"},"application/x-www-form-urlencoded")," and a ",Object(i.b)("inlineCode",{parentName:"p"},"logout_token")," will be\nmade to that URL when a end-user logs out. The ",Object(i.b)("inlineCode",{parentName:"p"},"logout_token")," is a JWT signed\nwith the same key that is used to sign OpenID Connect ID Tokens. You should thus\nvalidate the ",Object(i.b)("inlineCode",{parentName:"p"},"logout_token")," using the ID Token Public Key (can be fetched from\n",Object(i.b)("inlineCode",{parentName:"p"},"/.well-known/jwks.json"),"). The ",Object(i.b)("inlineCode",{parentName:"p"},"logout_token")," contains the following claims:"),Object(i.b)("ul",{parentName:"div"},Object(i.b)("li",{parentName:"ul"},Object(i.b)("inlineCode",{parentName:"li"},"iss")," - Issuer Identifier, as specified in Section 2 of ","[OpenID.Core]","."),Object(i.b)("li",{parentName:"ul"},Object(i.b)("inlineCode",{parentName:"li"},"aud")," - Audience(s), as specified in Section 2 of ","[OpenID.Core]","."),Object(i.b)("li",{parentName:"ul"},Object(i.b)("inlineCode",{parentName:"li"},"iat")," - Issued at time, as specified in Section 2 of ","[OpenID.Core]","."),Object(i.b)("li",{parentName:"ul"},Object(i.b)("inlineCode",{parentName:"li"},"jti")," - Unique identifier for the token, as specified in Section 9 of\n","[OpenID.Core]","."),Object(i.b)("li",{parentName:"ul"},Object(i.b)("inlineCode",{parentName:"li"},"events")," - Claim whose value is a JSON object containing the member name\n",Object(i.b)("a",Object(a.a)({parentName:"li"},{href:"http://schemas.openid.net/event/backchannel-logout"}),"http://schemas.openid.net/event/backchannel-logout"),". This declares that the JWT\nis a Logout Token. The corresponding member value MUST be a JSON object and\nSHOULD be the empty JSON object {}."),Object(i.b)("li",{parentName:"ul"},Object(i.b)("inlineCode",{parentName:"li"},"sid")," - Session ID - String identifier for a Session. This represents a\nSession of a User Agent or device for a logged-in End-User at an RP. Different\nsid values are used to identify distinct sessions at an OP. The sid value need\nonly be unique in the context of a particular issuer. Its contents are opaque\nto the RP. Its syntax is the same as an OAuth 2.0 Client Identifier.")),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{}),'{\n  "iss": "https://server.example.com",\n  "aud": "s6BhdRkqt3",\n  "iat": 1471566154,\n  "jti": "bWJq",\n  "sid": "08a5019c-17e1-4977-8f42-65a12843ea02",\n  "events": {\n     "http://schemas.openid.net/event/backchannel-logout": {}\n   }\n}\n')),Object(i.b)("p",{parentName:"div"},"An exemplary Back-Channel Logout Request looks as follows:"),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{}),"POST /backchannel_logout HTTP/1.1\nHost: rp.example.org\nContent-Type: application/x-www-form-urlencoded\n\nlogout_token=eyJhbGci ... .eyJpc3Mi ... .T3BlbklE ...\n")),Object(i.b)("p",{parentName:"div"},"The Logout Token must be validated as follows:"),Object(i.b)("ul",{parentName:"div"},Object(i.b)("li",{parentName:"ul"},"Validate the Logout Token signature in the same way that an ID Token signature\nis validated, with the following refinements."),Object(i.b)("li",{parentName:"ul"},"Validate the iss, aud, and iat Claims in the same way they are validated in ID\nTokens."),Object(i.b)("li",{parentName:"ul"},"Verify that the Logout Token contains a sid Claim."),Object(i.b)("li",{parentName:"ul"},"Verify that the Logout Token contains an events Claim whose value is JSON\nobject containing the member name\n",Object(i.b)("a",Object(a.a)({parentName:"li"},{href:"http://schemas.openid.net/event/backchannel-logout"}),"http://schemas.openid.net/event/backchannel-logout"),"."),Object(i.b)("li",{parentName:"ul"},"Verify that the Logout Token does not contain a nonce Claim."),Object(i.b)("li",{parentName:"ul"},"Optionally verify that another Logout Token with the same jti value has not\nbeen recently received.")),Object(i.b)("p",{parentName:"div"},"The endpoint then returns a HTTP 200 OK response. Cache-Control headers should\nbe set to:"),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{}),"Cache-Control: no-cache, no-store\nPragma: no-cache\n")),Object(i.b)("p",{parentName:"div"},"Because the OpenID Connect Back-Channel Logout Flow is not executed using the\nuser-agent (e.g. Browser) but from ORY Hydra directly, the session cookie of the\nend-user will not be available to the OAuth 2.0 Client and the session has to be\ninvalidated by some other means (e.g. by blacklisting the session ID)."),Object(i.b)("h3",Object(a.a)({parentName:"div"},{id:"revoking-consent-and-login-sessions"}),"Revoking consent and login sessions"),Object(i.b)("h4",Object(a.a)({parentName:"div"},{id:"login"}),"Login"),Object(i.b)("p",{parentName:"div"},"You can revoke login sessions. Revoking a login session will remove all of the\nuser's cookies at ORY Hydra and will require the user to re-authenticate when\nperforming the next OAuth 2.0 Authorize Code Flow. Be aware that this option\nwill remove all cookies from all devices."),Object(i.b)("p",{parentName:"div"},"Revoking the login sessions of a user is as easy as sending\n",Object(i.b)("inlineCode",{parentName:"p"},"DELETE to"),"/oauth2/auth/sessions/login?subject={subject}`."),Object(i.b)("p",{parentName:"div"},"This endpoint is not compatible with OpenID Connect Front-/Backchannel logout\nand does not revoke any tokens."),Object(i.b)("h4",Object(a.a)({parentName:"div"},{id:"consent"}),"Consent"),Object(i.b)("p",{parentName:"div"},"You can revoke a user's consent either on a per application basis or for all\napplications. Revoking the consent will automatically revoke all related access\nand refresh tokens."),Object(i.b)("p",{parentName:"div"},"Revoking all consent sessions of a user is as easy as sending\n",Object(i.b)("inlineCode",{parentName:"p"},"DELETE to"),"/oauth2/auth/sessions/consent?subject={subject}`."),Object(i.b)("p",{parentName:"div"},"Revoking the consent sessions of a user for a specific client is as easy as\nsending\n",Object(i.b)("inlineCode",{parentName:"p"},"DELETE to"),"/oauth2/auth/sessions/consent?subject={subject}&client={client}`."),Object(i.b)("h2",Object(a.a)({parentName:"div"},{id:"oauth-20-1"}),"OAuth 2.0"),Object(i.b)("h3",Object(a.a)({parentName:"div"},{id:"oauth-20-scope"}),"OAuth 2.0 Scope"),Object(i.b)("p",{parentName:"div"},"The scope of an OAuth 2.0 scope defines the permission the token was granted by\nthe end-user. For example, a specific token might be allowed to access public\npictures, but not private ones. The granted permissions are established during\nthe consent screen."),Object(i.b)("p",{parentName:"div"},"Additionally, ORY Hydra has pre-defined OAuth 2.0 Scope values:"),Object(i.b)("ul",{parentName:"div"},Object(i.b)("li",{parentName:"ul"},Object(i.b)("inlineCode",{parentName:"li"},"offline_access"),": Include this scope if you wish to receive a refresh token"),Object(i.b)("li",{parentName:"ul"},Object(i.b)("inlineCode",{parentName:"li"},"openid"),": Include this scope if you wish to perform an OpenID Connect request.")),Object(i.b)("p",{parentName:"div"},"When performing an OAuth 2.0 Flow where the end-user is involved (e.g. Implicit\nor Authorize Code), the granted OAuth 2.0 Scope must be set when accepting the\nconsent using the ",Object(i.b)("inlineCode",{parentName:"p"},"grant_scope")," key."),Object(i.b)("blockquote",{parentName:"div"},Object(i.b)("p",{parentName:"blockquote"},"A OAuth 2.0 Scope ",Object(i.b)("strong",{parentName:"p"},"is not a permission"),":"),Object(i.b)("ul",{parentName:"blockquote"},Object(i.b)("li",{parentName:"ul"},"A permission allows an actor to perform a certain action in a system: ",Object(i.b)("em",{parentName:"li"},"Bob\nis allowed to delete his own photos"),"."),Object(i.b)("li",{parentName:"ul"},"OAuth 2.0 Scope implies that an end-user granted certain privileges to a\nclient: ",Object(i.b)("em",{parentName:"li"},"Bob allowed the OAuth 2.0 Client to delete all users"),".")),Object(i.b)("p",{parentName:"blockquote"},'The OAuth 2.0 Scope can be granted without the end-user actually having the\nright permissions. In the examples above, Bob granted an OAuth 2.0 Client the\npermission ("scope") to delete all users in his name. However, since Bob is\nnot an administrator, that permission ("access control") is not actually\ngranted to Bob. Therefore any request by the OAuth 2.0 Client that tries to\ndelete users on behalf of Bob should fail.')),Object(i.b)("h3",Object(a.a)({parentName:"div"},{id:"oauth-20-access-token-audience"}),"OAuth 2.0 Access Token Audience"),Object(i.b)("p",{parentName:"div"},"The Audience of an Access Token refers to the Resource Servers that this token\nis intended for. The audience usually refers to one or more URLs such as"),Object(i.b)("ul",{parentName:"div"},Object(i.b)("li",{parentName:"ul"},Object(i.b)("inlineCode",{parentName:"li"},"https://api.mydomain.com/blog/posts")),Object(i.b)("li",{parentName:"ul"},Object(i.b)("inlineCode",{parentName:"li"},"https://api.mydomain.com/users"))),Object(i.b)("p",{parentName:"div"},"but may also refer to a subset of resources:"),Object(i.b)("ul",{parentName:"div"},Object(i.b)("li",{parentName:"ul"},Object(i.b)("inlineCode",{parentName:"li"},"https://api.mydomain.com/tenants/foo/users"))),Object(i.b)("p",{parentName:"div"},"When performing an OAuth 2.0 Flow where the end-user is involved (e.g. Implicit\nor Authorize Code), the granted audience must be set when accepting the consent\nusing the ",Object(i.b)("inlineCode",{parentName:"p"},"grant_access_token_audience")," key. In most cases, it is ok to grant\nthe audience without user-interaction."),Object(i.b)("h3",Object(a.a)({parentName:"div"},{id:"oauth-20-refresh-tokens"}),"OAuth 2.0 Refresh Tokens"),Object(i.b)("p",{parentName:"div"},"OAuth 2.0 Refresh Tokens are issued only when an Authorize Code Flow\n(",Object(i.b)("inlineCode",{parentName:"p"},"response_type=code"),") or an OpenID Connect Hybrid Flow with an Authorize Code\nResponse Type (",Object(i.b)("inlineCode",{parentName:"p"},"response_type=code+..."),") is executed. OAuth 2.0 Refresh Tokens\nare not returned for Implicit or Client Credentials grants:"),Object(i.b)("p",{parentName:"div"},"Capable of issuing an OAuth 2.0 Refresh Token:"),Object(i.b)("ul",{parentName:"div"},Object(i.b)("li",{parentName:"ul"},Object(i.b)("a",Object(a.a)({parentName:"li"},{href:"https://ory-hydra.example/oauth2/auth?response_type=code&.."}),"https://ory-hydra.example/oauth2/auth?response_type=code&.."),"."),Object(i.b)("li",{parentName:"ul"},Object(i.b)("a",Object(a.a)({parentName:"li"},{href:"https://ory-hydra.example/oauth2/auth?response_type=code+token&.."}),"https://ory-hydra.example/oauth2/auth?response_type=code+token&.."),"."),Object(i.b)("li",{parentName:"ul"},Object(i.b)("a",Object(a.a)({parentName:"li"},{href:"https://ory-hydra.example/oauth2/auth?response_type=code+token+id_token&.."}),"https://ory-hydra.example/oauth2/auth?response_type=code+token+id_token&.."),"."),Object(i.b)("li",{parentName:"ul"},Object(i.b)("a",Object(a.a)({parentName:"li"},{href:"https://ory-hydra.example/oauth2/auth?response_type=code+id_token&.."}),"https://ory-hydra.example/oauth2/auth?response_type=code+id_token&.."),".")),Object(i.b)("p",{parentName:"div"},"Will not issue an OAuth 2.0 Refresh Token:"),Object(i.b)("ul",{parentName:"div"},Object(i.b)("li",{parentName:"ul"},Object(i.b)("a",Object(a.a)({parentName:"li"},{href:"https://ory-hydra.example/oauth2/auth?response_type=token&.."}),"https://ory-hydra.example/oauth2/auth?response_type=token&.."),"."),Object(i.b)("li",{parentName:"ul"},Object(i.b)("a",Object(a.a)({parentName:"li"},{href:"https://ory-hydra.example/oauth2/auth?response_type=token+id_token&.."}),"https://ory-hydra.example/oauth2/auth?response_type=token+id_token&.."),"."),Object(i.b)("li",{parentName:"ul"},Object(i.b)("a",Object(a.a)({parentName:"li"},{href:"https://ory-hydra.example/oauth2/token?grant_type=client_redentials&.."}),"https://ory-hydra.example/oauth2/token?grant_type=client_redentials&.."),".")),Object(i.b)("p",{parentName:"div"},"Additionally, each OAuth 2.0 Client that wants to request an OAuth 2.0 Refresh\nToken must be allowed to request scope ",Object(i.b)("inlineCode",{parentName:"p"},"offline_access"),". When performing an\nOAuth 2.0 Authorize Code Flow, the ",Object(i.b)("inlineCode",{parentName:"p"},"offline_access")," value must be included in\nthe requested OAuth 2.0 Scope:"),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{}),"https://authorization-server.com/auth\n &scope=offline_access\n ?response_type=code\n &client_id=...\n &redirect_uri=...\n &state=...\n")),Object(i.b)("p",{parentName:"div"},"When accepting the consent request, ",Object(i.b)("inlineCode",{parentName:"p"},"offline_access")," must be in the list of\n",Object(i.b)("inlineCode",{parentName:"p"},"grant_scope"),":"),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{}),"fetch('https://hydra/oauth2/auth/requests/consent/accept?challenge=' + encodeURIComponent(challenge), {\n    method: 'PUT',\n    body: JSON.stringify(body),\n    headers: { 'Content-Type': 'application/json' }\n}).\nconst body = {\n    grant_scope: [\"offline_access\"],\n}\n")),Object(i.b)("p",{parentName:"div"},"Refresh Token Lifespan can be set using configuration key ",Object(i.b)("inlineCode",{parentName:"p"},"ttl.refresh_token"),".\nIf set to -1, Refresh Tokens never expire."),Object(i.b)("h3",Object(a.a)({parentName:"div"},{id:"oauth-20-token-introspection"}),"OAuth 2.0 Token Introspection"),Object(i.b)("p",{parentName:"div"},"OAuth2 Token Introspection is an ",Object(i.b)("a",Object(a.a)({parentName:"p"},{href:"https://tools.ietf.org/html/rfc7662"}),"IETF"),"\nstandard. It defines a method for a protected resource to query an OAuth 2.0\nauthorization server to determine the active state of an OAuth 2.0 token and to\ndetermine meta-information about this token. OAuth 2.0 deployments can use this\nmethod to convey information about the authorization context of the token from\nthe authorization server to the protected resource."),Object(i.b)("p",{parentName:"div"},"The usage of an access token or client credentials is required to access the\nendpoint. ORY Hydra will however accept any valid token or valid credentials as\nthere is no built-in access control."),Object(i.b)("p",{parentName:"div"},"You can find more details on this endpoint in the\n",Object(i.b)("a",Object(a.a)({parentName:"p"},{href:"https://www.ory.sh/docs/"}),"ORY Hydra API Docs"),". You can also use the CLI command\n",Object(i.b)("inlineCode",{parentName:"p"},"hydra token introspect <token>"),"."),Object(i.b)("h3",Object(a.a)({parentName:"div"},{id:"oauth-20-clients"}),"OAuth 2.0 Clients"),Object(i.b)("p",{parentName:"div"},"You can manage ",Object(i.b)("em",{parentName:"p"},"OAuth 2.0 clients")," using the cli or the HTTP REST API:"),Object(i.b)("ul",{parentName:"div"},Object(i.b)("li",{parentName:"ul"},Object(i.b)("strong",{parentName:"li"},"CLI:")," ",Object(i.b)("inlineCode",{parentName:"li"},"hydra help clients")),Object(i.b)("li",{parentName:"ul"},Object(i.b)("strong",{parentName:"li"},"REST:")," Read the ",Object(i.b)("a",Object(a.a)({parentName:"li"},{href:"https://www.ory.sh/docs/hydra/sdk/api"}),"API Docs"))),Object(i.b)("h4",Object(a.a)({parentName:"div"},{id:"examples"}),"Examples"),Object(i.b)("p",{parentName:"div"},"This section provides a few examples to get you started with the most-used OAuth\n2.0 Clients:"),Object(i.b)("h5",Object(a.a)({parentName:"div"},{id:"authorize-code-flow-with-refresh-token"}),"Authorize Code Flow with Refresh Token"),Object(i.b)("p",{parentName:"div"},"The following command creates an OAuth 2.0 Client capable of executing the\nAuthorize Code Flow, requesting ID and Refresh Tokens and performing the OAuth\n2.0 Refresh Grant:"),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{className:"language-sh"}),"hydra clients create \\\n    --endpoint http://ory-hydra:4445 \\\n    --id client-id \\\n    --secret client-secret \\\n    --grant-types authorization_code,refresh_token \\\n    --response-types code \\\n    --scope openid,offline \\\n    --callbacks http://my-app.com/callback,http://my-other-app.com/callback\n")),Object(i.b)("p",{parentName:"div"},"The OAuth 2.0 Client will be allowed to use values ",Object(i.b)("inlineCode",{parentName:"p"},"http://my-app.com/callback"),"\nand ",Object(i.b)("inlineCode",{parentName:"p"},"http://my-other-app.com/callback")," as ",Object(i.b)("inlineCode",{parentName:"p"},"redirect_url"),"."),Object(i.b)("blockquote",{parentName:"div"},Object(i.b)("p",{parentName:"blockquote"},"It is expected that the OAuth 2.0 Client sends its credentials using HTTP\nBasic Authorization.")),Object(i.b)("p",{parentName:"div"},"If you wish to send credentials in the POST Body, add the following flag to the\ncommand above:"),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{}),"    --token-endpoint-auth-method client_secret_post \\\n")),Object(i.b)("p",{parentName:"div"},"The same can be achieved by setting\n",Object(i.b)("inlineCode",{parentName:"p"},'"token_endpoint_auth_method": "client_secret_post"')," in the request body of\n",Object(i.b)("inlineCode",{parentName:"p"},"POST /clients")," and ",Object(i.b)("inlineCode",{parentName:"p"},"PUT /clients/<id>"),"."),Object(i.b)("h5",Object(a.a)({parentName:"div"},{id:"client-credentials-flow"}),"Client Credentials Flow"),Object(i.b)("p",{parentName:"div"},"A client only capable of performing the Client Credentials Flow can be created\nas follows:"),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{}),"hydra clients create \\\n    --endpoint http://ory-hydra:4445 \\\n    --id my-client \\\n    --secret secret \\\n    -g client_credentials\n")),Object(i.b)("h2",Object(a.a)({parentName:"div"},{id:"openid-connect"}),"OpenID Connect"),Object(i.b)("h3",Object(a.a)({parentName:"div"},{id:"userinfo"}),"Userinfo"),Object(i.b)("p",{parentName:"div"},"The ",Object(i.b)("inlineCode",{parentName:"p"},"/userinfo")," endpoint returns information on a user given an access token.\nSince ORY Hydra is agnostic to any end-user data, the ",Object(i.b)("inlineCode",{parentName:"p"},"/userinfo")," endpoint\nreturns only minimal information per default:"),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{}),'GET https://ory-hydra:4444/userinfo\nAuthorization: bearer access-token.xxxx\n\n{\n "acr": "oauth2",\n "sub": "xxx@xxx.com"\n}\n')),Object(i.b)("p",{parentName:"div"},"Any information set to the key ",Object(i.b)("inlineCode",{parentName:"p"},"session.id_token")," during accepting the consent\nrequest will also be included here."),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{className:"language-js"}),"\n// This is node-js pseudo code and will not work if you copy it 1:1\n\nconst body = {\n    // grant_scope: [\"foo\", \"bar\"],\n    // ...\n    session: {\n        id_token: {\n            \"foo\": \"bar\"\n        },\n    }\n}\n\nfetch('https://hydra/oauth2/auth/requests/consent/' + challenge + '/accept', {\n    method: 'PUT',\n    body: JSON.stringify(body),\n    headers: { 'Content-Type': 'application/json' }\n}).\n    // then(function (response) {\n")),Object(i.b)("p",{parentName:"div"},"By making the ",Object(i.b)("inlineCode",{parentName:"p"},"/userinfo")," call with a token issued by this consent request, one\nwould receive:"),Object(i.b)("pre",{parentName:"div"},Object(i.b)("code",Object(a.a)({parentName:"pre"},{}),'GET https://ory-hydra:4444/userinfo\nAuthorization: bearer new-access-token.xxxx\n\n{\n "acr": "oauth2",\n "sub": "xxx@xxx.com",\n "foo": "bar"\n}\n')),Object(i.b)("p",{parentName:"div"},"You should only include data that has been authorized by the end-user through an\nOAuth 2.0 Scope. If an OAuth 2.0 Client, for example, requests the ",Object(i.b)("inlineCode",{parentName:"p"},"phone")," scope\nand the end-user authorizes that scope, the phone number should be added to\n",Object(i.b)("inlineCode",{parentName:"p"},"session.id_token"),"."),Object(i.b)("blockquote",{parentName:"div"},Object(i.b)("p",{parentName:"blockquote"},"Be aware that the ",Object(i.b)("inlineCode",{parentName:"p"},"/userinfo")," endpoint is public. Its contents are thus as\npublicly visible as those of ID Tokens. It is therefore imperative to ",Object(i.b)("strong",{parentName:"p"},"not\nexpose sensitive information without end-user consent."))))))}h.isMDXComponent=!0},224:function(e,t,n){"use strict";n.d(t,"a",(function(){return p})),n.d(t,"b",(function(){return u}));var a=n(0),o=n.n(a);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function s(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function c(e,t){if(null==e)return{};var n,a,o=function(e,t){if(null==e)return{};var n,a,o={},i=Object.keys(e);for(a=0;a<i.length;a++)n=i[a],t.indexOf(n)>=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a<i.length;a++)n=i[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var l=o.a.createContext({}),h=function(e){var t=o.a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):s({},t,{},e)),n},p=function(e){var t=h(e.components);return o.a.createElement(l.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.a.createElement(o.a.Fragment,{},t)}},b=Object(a.forwardRef)((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,r=e.parentName,l=c(e,["components","mdxType","originalType","parentName"]),p=h(n),b=a,u=p["".concat(r,".").concat(b)]||p[b]||d[b]||i;return n?o.a.createElement(u,s({ref:t},l,{components:n})):o.a.createElement(u,s({ref:t},l))}));function u(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,r=new Array(i);r[0]=b;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s.mdxType="string"==typeof e?e:a,r[1]=s;for(var l=2;l<i;l++)r[l]=n[l];return o.a.createElement.apply(null,r)}return o.a.createElement.apply(null,n)}b.displayName="MDXCreateElement"}}]);