From 9cb48486ca5ce6c5868a5e2d67faf1e524e6faa1 Mon Sep 17 00:00:00 2001 From: Bogdan Carpusor Date: Tue, 17 Sep 2024 16:41:20 +0300 Subject: [PATCH] Add oauth docs --- v2/docusaurus.config.js | 14 ++ .../clients-without-dedicated-backend.mdx | 138 +++++++++++++++ v2/oauth/introduction.mdx | 74 ++++++++ .../machine-to-machine-authentication.mdx | 127 ++++++++++++++ ...ltiple-frontends-with-a-single-backend.mdx | 160 ++++++++++++++++++ ...tiple-frontends-with-separate-backends.mdx | 149 ++++++++++++++++ v2/oauth/sidebars.js | 16 ++ v2/src/theme/NavbarItem/recipeSelector.js | 11 ++ 8 files changed, 689 insertions(+) create mode 100644 v2/oauth/clients-without-dedicated-backend.mdx create mode 100644 v2/oauth/introduction.mdx create mode 100644 v2/oauth/machine-to-machine-authentication.mdx create mode 100644 v2/oauth/multiple-frontends-with-a-single-backend.mdx create mode 100644 v2/oauth/multiple-frontends-with-separate-backends.mdx create mode 100644 v2/oauth/sidebars.js diff --git a/v2/docusaurus.config.js b/v2/docusaurus.config.js index 8904ac4ca..5510a811e 100644 --- a/v2/docusaurus.config.js +++ b/v2/docusaurus.config.js @@ -256,6 +256,20 @@ module.exports = { beforeDefaultRemarkPlugins, }, ], + [ + "@docusaurus/plugin-content-docs", + { + id: "oauth", + path: "oauth", + routeBasePath: "docs/oauth", + sidebarPath: require.resolve("./oauth/sidebars.js"), + showLastUpdateTime: true, + editUrl: "https://github.com/supertokens/docs/tree/master/v2/", + remarkPlugins: remarkPlugins, + rehypePlugins: rehypePlugins, + beforeDefaultRemarkPlugins, + }, + ], [ "@docusaurus/plugin-content-docs", { diff --git a/v2/oauth/clients-without-dedicated-backend.mdx b/v2/oauth/clients-without-dedicated-backend.mdx new file mode 100644 index 000000000..1b070262b --- /dev/null +++ b/v2/oauth/clients-without-dedicated-backend.mdx @@ -0,0 +1,138 @@ +--- +title: Clients without dedicated backends +hide_title: true +--- + +# Clients Without Dedicated Backends + +You can use the following guide if you have frontend clients that use a separate domain for authentication but do not have dedicated backends for other functionalities. + +## 1. Set Up Your Authentication Portal + +First, initialize and setup **SuperTokens** according to the [quick setup guide](#). + +## 2. Initialize the OAuth2Provider + +In addition to the authentication recipes you’ve set up, initialize the OAuth2Provider recipe on your backend service. +The default settings should be sufficient for most use cases. + +```typescript +import supertokens from "supertokens-node"; +import OAuth2Provider from"supertokens-node/recipe/oauth2"; + +supertokens.init({ + framework: "express", + supertokens: { + connectionURI: "", + apiKey: "", + }, + appInfo: { + appName: "", + apiDomain: "", + websiteDomain: "", + apiBasePath: "/auth", + websiteBasePath: "/auth" + }, + recipeList: [ + // This is where you initialize the OAuth2 recipe + // TODO: Show the config parameters here + OAuth2Provider.init(), + ] +}); +``` + +## 3. Add a Way to Create OAuth2 Clients + +We have to create OAuth2 clients in order to allow them to authenticate through our flow. + +First we need to add a method to do this. +Our recommendation is to use the `createClient` function that is exposed by our SDK. +Alternatively, you can also directly call the **SuperTokens** core API. + +### Using the `createClient` function + +```typescript +import { createClient } from "supertokens-node/recipe/oauth2"; + +createClient({ + // This value will be shown in the auth page when the app requests the user to authenticate + clientName: 'My Application', + // TODO: Add info about this + responseTypes: ['code', 'id_token'], + // TODO: Add info about this + grantTypes: ['authorization_code', 'refresh_token'], + // TODO: Add info about this + redirectUri: 'https://example.com/oauth/callback', +}); + +``` + +### Using the Core API + +```bash +## TODO: This is a dummy example. +curl -X POST https://api.auth.example.com/oauth2/clients \ +-H "Content-Type: application/json" \ +-d '{ + "client_name": "My Application", + "response_types": ["code", "id_token"], + "grant_types": ["authorization_code", "refresh_token"], + "redirect_uris": ["https://different.com/oauth/callback"] +}' +``` + +## 4. Create Separate Clients for Each Application + +Using the method that we defined in the previous step create separate clients for each of your applications. + +Repeat the process for each domain, adjusting the `clientName` and `redirectUri` as necessary. + + +## 5. Set Up Your Frontend and Integrate with an OIDC Library + +Integrate your frontend applications with a generic OAuth2 or OIDC library. Here are a few popular options: [oidc-client-js](https://github.com/IdentityModel/oidc-client-js), [openid-client](https://github.com/panva/node-openid-client) + +### Example Using `oidc-client-js` + +```html + + + + + + +
+``` + diff --git a/v2/oauth/introduction.mdx b/v2/oauth/introduction.mdx new file mode 100644 index 000000000..e6078a7ba --- /dev/null +++ b/v2/oauth/introduction.mdx @@ -0,0 +1,74 @@ +--- +title: Introduction +hide_title: true +--- + +# OAuth2 Support + +**OAuth2**, Open Authorization, is an industry-standard authorization framework that enables third-party applications to obtain limited access to a user's resources without exposing their credentials. +It is commonly used for single sign-on (SSO) and authorization. + +## When to use OAuth2 + +In most cases, when using **SuperTokens**, you should not have to use OAuth2 directly. +We already expose the `ThirdParty` recipe that enables users to login using external providers. +This covers most of the authentication scenarios that involve OAuth2. + +However, there are some custom use cases where you will have to implement a different authentication flow. + +- **[If you have multiple frontends connecting to the same backend](/docs)** +- **[If you want to have an unified login experience across multiple domains](/docs)** +- **[If you want to reuse your website login for desktop and mobile apps](/docs)** +- **[For Machine to Machine communication](/docs)** + +For these specific instances we expose an `OAuth` recipe that allows you to complete your setup. + + +## OAuth2 Reference + +Before we explore our OAuth2 implementation let's first recap some common terms and concepts that are used in the framework. +We will use them throughout the next pages. + +### Clients + +An OAuth 2.0 client is an application that interacts with an OAuth 2.0 provider to request access to a user's resources. +It then utilizes the access tokens granted by the provider to perform authorized operations on behalf of the user. + +The term **client** does not imply any particular implementation characteristics (e.g. whether the application executes on a server, a desktop, or other devices). + + +### Tokens + +An OAuth token is a credential used to access protected resources on behalf of a user. + +#### Access Token + +Access tokens are what the OAuth client uses to make requests to an API. The access token is meant to be read and validated by the API. + +#### ID Token + +An ID token contains information about what happened when a user authenticated, and is intended to be read by the OAuth client. +The ID token may also contain information about the user such as their name or email address, although that is not a requirement of an ID token. + +#### Refresh Token + +An OAuth Refresh Token is a string that the OAuth client can use to get a new access token without the user's interaction. + +### Flows + +Flows are the set of steps a Client has to perform in order to obtain an access token. +Our implementation supports the following grant types: +- [Authorization Code](https://datatracker.ietf.org/doc/html/rfc6749#section-1.3.1) +- [Implicit](https://datatracker.ietf.org/doc/html/rfc6749#section-1.3.2) +- [Client credentials](https://datatracker.ietf.org/doc/html/rfc6749#section-1.3.4) + +### Scopes + +Scopes in OAuth 2.0 specify the level of access an application is requesting from the user's resources. +This way you can define the boundaries and permissions for what the access token can do. + +### PKCE + +PKCE (RFC 7636) is an extension to the Authorization Code flow to prevent CSRF and authorization code injection attacks. https://oauth.net/2/pkce/ +We support PKCE and recommend that you use a library that makes use of it. +You can opt-in by setting the code_challenge (and optionally code_challenge_method) parameters in the authorization URL and then verifying it after the user gets redirected back to the client. diff --git a/v2/oauth/machine-to-machine-authentication.mdx b/v2/oauth/machine-to-machine-authentication.mdx new file mode 100644 index 000000000..e595243a9 --- /dev/null +++ b/v2/oauth/machine-to-machine-authentication.mdx @@ -0,0 +1,127 @@ +--- +title: Machine to Machine Authentication +hide_title: true +--- + +# Machine to Machine Authentication + +You can use the following guide if you want to implement a custom authentication flow between your microservices. + +## 1. Set Up Your Authentication Portal + +First, initialize and setup **SuperTokens** according to the [quick setup guide](#). + +## 2. Initialize the OAuth2Provider + +In addition to the authentication recipes you’ve set up, initialize the OAuth2Provider recipe on your backend service. +The default settings should be sufficient for most use cases. + +```typescript +import supertokens from "supertokens-node"; +import OAuth2Provider from"supertokens-node/recipe/oauth2"; + +supertokens.init({ + framework: "express", + supertokens: { + connectionURI: "", + apiKey: "", + }, + appInfo: { + appName: "", + apiDomain: "", + websiteDomain: "", + apiBasePath: "/auth", + websiteBasePath: "/auth" + }, + recipeList: [ + // This is where you initialize the OAuth2 recipe + // TODO: Show the config parameters here + OAuth2Provider.init(), + ] +}); +``` + +## 3. Add a Way to Create OAuth2 Clients + +We have to create OAuth2 clients in order to allow them to authenticate through our flow. + +First we need to add a method to do this. +Our recommendation is to use the `createClient` function that is exposed by our SDK. +Alternatively, you can also directly call the **SuperTokens** core API. + +### Using the `createClient` function + +```typescript +import { createClient } from "supertokens-node/recipe/oauth2"; + +createClient({ + // This value will be shown in the auth page when the app requests the user to authenticate + clientName: 'My Application', + // TODO: Add info about this + responseTypes: ['code', 'id_token'], + // TODO: Add info about this + grantTypes: ['client_credentials'], +}); + +``` + +### Using the Core API + +```bash +## TODO: This is a dummy example. +curl -X POST https://api.auth.example.com/oauth2/clients \ +-H "Content-Type: application/json" \ +-d '{ + "client_name": "My Application", + "response_types": ["code", "id_token"], + "grant_types": ["client_credentials"] +}' +``` + +## 4. Create Separate Clients for Each Application + +Using the method that we defined in the previous step create separate clients for each of your applications/microservices. + +Repeat the process for each domain, adjusting the `clientName`. + +## 5. How to Create Tokens + +To generate tokens for your microservices using the `client_credentials` grant type, use the following code snippet: + +```typescript +const generateToken = async (client, scope) => { + try { + const res = await fetch('https://api.auth.example.com/auth/oauth/token', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + client_id: client.clientId, + client_secret: client.clientSecret, + grant_type: 'client_credentials', + scope, + }), + }); + + const tokenData = await res.json(); + if (!res.ok) { + throw new Error(`Error fetching token: ${tokenData.error_description}`); + } + + console.log('Token created successfully:', tokenData); + } catch (error) { + console.error('Error creating token:', error); + } +}; + +// Example usage +const client = { + clientId: 'YOUR_CLIENT_ID', + clientSecret: 'YOUR_CLIENT_SECRET', +}; + +generateToken(client, 'your-desired-scope'); +``` + + diff --git a/v2/oauth/multiple-frontends-with-a-single-backend.mdx b/v2/oauth/multiple-frontends-with-a-single-backend.mdx new file mode 100644 index 000000000..eebd602ec --- /dev/null +++ b/v2/oauth/multiple-frontends-with-a-single-backend.mdx @@ -0,0 +1,160 @@ +--- +title: Multiple frontend domains with a common backend +hide_title: true +--- + +import BackendSDKTabs from "/src/components/tabs/BackendSDKTabs" + + +# Multiple Frontend Domains Using a Single Backend + +You can use the following guide if you have multiple frontend domains that use the same backend. + +## 1. Set Up Your Authentication Portal + +First, initialize and setup **SuperTokens** according to the [quick setup guide](#). + +## 2. Initialize the OAuth2Provider + +In addition to the authentication recipes you’ve set up, initialize the OAuth2Provider recipe on your backend service. +The default settings should be sufficient for most use cases. + +```typescript +import supertokens from "supertokens-node"; +import OAuth2Provider from"supertokens-node/recipe/oauth2"; + +supertokens.init({ + framework: "express", + supertokens: { + connectionURI: "", + apiKey: "", + }, + appInfo: { + appName: "", + apiDomain: "", + websiteDomain: "", + apiBasePath: "/auth", + websiteBasePath: "/auth" + }, + recipeList: [ + // This is where you initialize the OAuth2 recipe + // TODO: Show the config parameters here + OAuth2Provider.init(), + ] +}); +``` + +## 3. Add a Way to Create OAuth2 Clients + +We have to create OAuth2 clients in order to allow them to authenticate through our flow. + +First we need to add a method to do this. +Our recommendation is to use the `createClient` function that is exposed by our SDK. +Alternatively, you can also directly call the **SuperTokens** core API. + +### Using the `createClient` function + +```typescript +import { createClient } from "supertokens-node/recipe/oauth2"; + +createClient({ + // This value will be shown in the auth page when the app requests the user to authenticate + clientName: 'My Application', + // TODO: Add info about this + responseTypes: ['code', 'id_token'], + // TODO: Add info about this + grantTypes: ['authorization_code', 'refresh_token'], + // TODO: Add info about this + redirectUri: 'https://example.com/oauth/callback', +}); + +``` + + +### Using the Core API + +```bash +## TODO: This is a dummy example. +curl -X POST https://api.auth.example.com/oauth2/clients \ +-H "Content-Type: application/json" \ +-d '{ + "client_name": "My Application", + "response_types": ["code", "id_token"], + "grant_types": ["authorization_code", "refresh_token"], + "redirect_uris": ["https://different.com/oauth/callback"] +}' +``` + +## 4. Create Separate Clients for Each Application + +Using the method that we defined in the previous step create separate clients for each of your applications. + +Repeat the process for each domain, adjusting the `clientName` and `redirectUri` as necessary. + +## 5. Configure OAuth2Client recipe in the Backend + +Update the backend configuration to also initialize the `OAuth2Client` recipe and add the created clients to the client array. + +```typescript +import supertokens from "supertokens-node"; +import { OAuth2Client } from"supertokens-node/recipe/oauth2"; + +supertokens.init({ + framework: "express", + supertokens: { + connectionURI: "", + apiKey: "", + }, + appInfo: { + appName: "", + apiDomain: "", + websiteDomain: "", + apiBasePath: "/auth", + websiteBasePath: "/auth" + }, + recipeList: [ + OAuth2Provider.init(), + // This is where you initialize the OAuth2Client recipe + OAuth2Client.init([ + {clientId: 'CLIENT_ID_A'}, + {clientId: 'CLIENT_ID_B'} + ]), + ] +}); +``` + +## 6. Initialize the st-website SDK on the Frontend + +Initialize the st-website SDK on the frontend of each application, ensuring you set the `authMode` to `header`. + +```typescript +SuperTokens.init({ + appInfo: { + appName: "", + apiDomain: "", + websiteDomain: "", + apiBasePath: "/auth", + websiteBasePath: "/auth" + }, + recipeList: [ + ] +}); +``` + +## 7. Add the Login UI in your frontend + +In your frontend application you will have to add a login button/link that will redirect the user to the authentication page. +Here's a basic example using a link: +``` html + + Login with OAuth2 + +``` + +The authentication URL needs to adhere to the following format: +- `` - The domain of the authentication backend. The example assumes that you didn't overwrite the default `apiBasePath`. +If that's the case you will have to replace `/auth` with you custom base path. +- `` - The client ID of the client you created in the previous step +- `` - The redirect URI of the client you created in the previous step + + diff --git a/v2/oauth/multiple-frontends-with-separate-backends.mdx b/v2/oauth/multiple-frontends-with-separate-backends.mdx new file mode 100644 index 000000000..b6a764027 --- /dev/null +++ b/v2/oauth/multiple-frontends-with-separate-backends.mdx @@ -0,0 +1,149 @@ +--- +title: Multiple frontend domains with separate backends +hide_title: true +--- + +import BackendSDKTabs from "/src/components/tabs/BackendSDKTabs" + + +# Multiple frontend domains with separate backends + +You can use the following guide if you have a single authentication portal with separated **client** applications. +Here's a login flow might look in this scenario: +1. User tries to access an application at the following domain `https://example-app.com`. +2. They get redirected to the authentication portal `https://auth.example.com`. +3. User completes the login action and is then redirected back to `https://example-app.com`. + +## 1. Set Up Your Authentication Portal + +First, initialize and setup **SuperTokens** according to the [quick setup guide](#). + +## 2. Initialize the OAuth2Provider + +In addition to the authentication recipes you’ve set up, initialize the OAuth2Provider recipe on your backend service. +The default settings should be sufficient for most use cases. + +```typescript +import supertokens from "supertokens-node"; +import OAuth2Provider from"supertokens-node/recipe/oauth2"; + +supertokens.init({ + framework: "express", + supertokens: { + connectionURI: "", + apiKey: "", + }, + appInfo: { + appName: "", + apiDomain: "", + websiteDomain: "", + apiBasePath: "/auth", + websiteBasePath: "/auth" + }, + recipeList: [ + // This is where you initialize the OAuth2 recipe + // TODO: Show the config parameters here + OAuth2Provider.init(), + ] +}); +``` + +## 3. Add a Way to Create OAuth2 Clients + +We have to create OAuth2 clients in order to allow them to authenticate through our flow. + +First we need to add a method to do this. +Our recommendation is to use the `createClient` function that is exposed by our SDK. +Alternatively, you can also directly call the **SuperTokens** core API. + +### Using the `createClient` function + +```typescript +import { createClient } from "supertokens-node/recipe/oauth2"; + +createClient({ + // This value will be shown in the auth page when the app requests the user to authenticate + clientName: 'My Application', + // TODO: Add info about this + responseTypes: ['code', 'id_token'], + // TODO: Add info about this + grantTypes: ['authorization_code', 'refresh_token'], + // TODO: Add info about this + redirectUri: 'https://example.com/oauth/callback', +}); + +``` + + +### Using the Core API + +```bash +## TODO: This is a dummy example. +curl -X POST https://api.auth.example.com/oauth2/clients \ +-H "Content-Type: application/json" \ +-d '{ + "client_name": "My Application", + "response_types": ["code", "id_token"], + "grant_types": ["authorization_code", "refresh_token"], + "redirect_uris": ["https://different.com/oauth/callback"] +}' +``` + +## 4. Create Separate Clients for Each Application + +Using the method that we defined in the previous step create separate clients for each of your applications. + +Repeat the process for each domain, adjusting the `clientName` and `redirectUri` as necessary. + +## 5. Set Up Session Handling in Each Application + +You will have to setup custom session handling logic in your backend. +You can use a generic OpenID Connect (OIDC) or OAuth2 library. +For `NodeJS` we recommend using either [passport-oauth2](https://www.passportjs.org/packages/passport-oauth2/) or [passport-openidconnect](https://www.passportjs.org/packages/passport-openidconnect/). + +#### Example using `passport-oauth2`: + +```typescript +// TODO: This is a dummy example. It will have to be adjusted +const passport = require('passport'); +const OAuth2Strategy = require('passport-oauth2'); + +passport.use(new OAuth2Strategy({ + authorizationURL: 'https://auth.example.com/oauth2/authorize', + tokenURL: 'https://api.auth.example.com/oauth2/token', + clientID: 'YOUR_CLIENT_ID', + clientSecret: 'YOUR_CLIENT_SECRET', + callbackURL: 'https://different.com/oauth/callback' +}, (accessToken, refreshToken, profile, cb) => { + User.findOrCreate({ oauthId: profile.id }, (err, user) => { + return cb(err, user); + }); +})); + +app.get('/auth/provider', + passport.authenticate('oauth2')); + +app.get('/auth/provider/callback', + passport.authenticate('oauth2', { failureRedirect: '/login' }), + (req, res) => { + res.redirect('/'); + }); +``` + +## 6. Add the Login UI in your frontend + +In your frontend application you will have to add a login button/link that will redirect the user to the authentication page. +Here's a basic example using a link: +``` html + + Login with OAuth2 + +``` + +The authentication URL needs to adhere to the following format: +- `` - The domain of the authentication backend. The example assumes that you didn't overwrite the default `apiBasePath`. +If that's the case you will have to replace `/auth` with you custom base path. +- `` - The client ID of the client you created in the previous step +- `` - The redirect URI of the client you created in the previous step + + diff --git a/v2/oauth/sidebars.js b/v2/oauth/sidebars.js new file mode 100644 index 000000000..56a7b5473 --- /dev/null +++ b/v2/oauth/sidebars.js @@ -0,0 +1,16 @@ +module.exports = { + sidebar: [ + "introduction", + { + type: "category", + label: "Use Cases", + collapsed: false, + items: [ + "multiple-frontends-with-separate-backends", + "multiple-frontends-with-a-single-backend", + "clients-without-dedicated-backend", + "machine-to-machine-authentication", + ], + }, + ], +}; diff --git a/v2/src/theme/NavbarItem/recipeSelector.js b/v2/src/theme/NavbarItem/recipeSelector.js index 505c328b5..b3a9679aa 100644 --- a/v2/src/theme/NavbarItem/recipeSelector.js +++ b/v2/src/theme/NavbarItem/recipeSelector.js @@ -38,6 +38,10 @@ export default function RecipeSelector(props) { return "User Management Dashboard"; case "multitenancy": return "Multi Tenancy"; + case "anomaly_detection": + return "Bot/Spam Detection"; + case "oauth": + return "OAuth"; default: return "Select Recipe"; } @@ -192,6 +196,13 @@ export default function RecipeSelector(props) { Bot / Spam Detection +
  • + OAuth +