From 0fefbd8a5695c138a0335ae0f516a2be01cac839 Mon Sep 17 00:00:00 2001 From: RichardJECooke Date: Mon, 28 Oct 2024 09:23:48 +0200 Subject: [PATCH 01/34] save --- .../authentication/service-to-service.mdx | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 astro/src/content/articles/authentication/service-to-service.mdx diff --git a/astro/src/content/articles/authentication/service-to-service.mdx b/astro/src/content/articles/authentication/service-to-service.mdx new file mode 100644 index 0000000000..823002753c --- /dev/null +++ b/astro/src/content/articles/authentication/service-to-service.mdx @@ -0,0 +1,72 @@ +--- +title: How service to service OAuth differs from user to service OAuth +description: How service to service OAuth differs from user to service OAuth +author: Richard Cooke +section: Authentication +tags: oauth, oauth2, service +icon: /img/icons/webauthn-explained.svg +darkIcon: /img/icons/webauthn-explained-dark.svg +--- + +- [Introduction](#introduction) +- [Differences From User OAuth](#differences-from-user-oauth) +- [Why Not Use Only A Username And Password?](#why-not-use-only-a-username-and-password) +- [Further Reading](#further-reading) +- [TODO](#todo) + + +## Introduction + + + client credentials grant + how to create/manage scopes + how to think about on behalf of calls + token expiration time + what kind of signing key to use (symmetric? asymmetric?) + +Have this playlist which helps some: https://www.youtube.com/watch?v=rT-VTtgligI&list=PLUOVyt5DYPpNzRdTKrio0P0a4mktqLPN9 + +## Differences From User OAuth + +- Client Credentials Grant flow, where the client application directly requests an access token using its client ID and secret. There is no user involved. +- Access tokens often have much shorter expiration times compared to user-granted OAuth tokens, since there is no user to re-authenticate. +- The client authentication can use a pre-established client ID and secret (symmetric key), or leverage public/private key pairs (asymmetric keys) for higher security. + +## Why Not Use Only A Username And Password? + +While username/password (basic auth) might seem simpler, using OAuth for service-to-service communication provides several crucial security benefits: + + Credential Management & Rotation + + With basic auth, changing passwords requires updating all services using them + OAuth allows you to rotate client secrets without service downtime + You can revoke specific tokens without changing the main credentials + + Fine-grained Access Control + + OAuth scopes let you limit what each service can do + Example: Service A might only need "read" access to users, while Service B needs "write" access + Basic auth is usually all-or-nothing access + + Audit Trail & Monitoring + + OAuth tokens can be tracked individually + You can see which service accessed what and when + With basic auth, multiple services sharing the same credentials are indistinguishable + + Standardized Security + + OAuth is a well-tested protocol with industry best practices + Many security tools and platforms are built to work with OAuth + Basic auth lacks features like token expiration and scope control + + Scalability + + As your system grows, OAuth makes it easier to manage hundreds of service-to-service connections + Each service can have its own client credentials with specific permissions + Basic auth becomes a maintenance nightmare at scale + + +## Further Reading + +## TODO \ No newline at end of file From 95746a5a3c0aacfb4afc2f099efc464e707ec251 Mon Sep 17 00:00:00 2001 From: RichardJECooke Date: Mon, 28 Oct 2024 11:31:57 +0200 Subject: [PATCH 02/34] explain normal oauth --- .../authentication/service-to-service.mdx | 94 ++++++++++++------- 1 file changed, 58 insertions(+), 36 deletions(-) diff --git a/astro/src/content/articles/authentication/service-to-service.mdx b/astro/src/content/articles/authentication/service-to-service.mdx index 823002753c..522ee1958b 100644 --- a/astro/src/content/articles/authentication/service-to-service.mdx +++ b/astro/src/content/articles/authentication/service-to-service.mdx @@ -9,64 +9,86 @@ darkIcon: /img/icons/webauthn-explained-dark.svg --- - [Introduction](#introduction) -- [Differences From User OAuth](#differences-from-user-oauth) -- [Why Not Use Only A Username And Password?](#why-not-use-only-a-username-and-password) +- [Understand user login](#understand-user-login) +- [Why Not Use Only A Username And Password For Machine Authorization?](#why-not-use-only-a-username-and-password-for-machine-authorization) - [Further Reading](#further-reading) - [TODO](#todo) - ## Introduction +This article explains how to use OAuth 2.0 and FusionAuth for service to service communication (which you can call machine OA). In other words, how to both provide and call an API programmatically, with no user logging in on a webpage. - client credentials grant - how to create/manage scopes - how to think about on behalf of calls - token expiration time - what kind of signing key to use (symmetric? asymmetric?) +## Understand user login -Have this playlist which helps some: https://www.youtube.com/watch?v=rT-VTtgligI&list=PLUOVyt5DYPpNzRdTKrio0P0a4mktqLPN9 +Let's start by summarizing how OAuth normally works for user login. You've most likely used FA for local login or third-party login. With local login, FA stores usernames and password hashes. With third-party login, another organization, like Google, handles authentication and FA is merely an intermediary. Either way, the authentication flow works as follows: -## Differences From User OAuth +- A user (resource owner) clicks "Log in" on your website (client). +- Your site redirects the user to the URL of the authorization server, which could either be FA directly, or another redirection to Google. This URL includes parameters for the Id of your site (client Id), the URL the user's browser should be sent to after logging in (redirect URI), the response type of code, and the permissions your site is requesting on behalf of the user (scopes). +- The user logs in and consents to the permissions requested on a page provided by the server. +- The authorization server redirects the browser back to your site (using the redirect URI), with a temporary authorization code. +- Your site then starts a separate HTTP call to the server directly. This is more secure than using the browser. Now your site can ask the server for a key (access token) to access the user's resources in future calls. In this call your site sends the temporary authorization code, your client Id, and your client secret. The server might instead return a refresh token with a long duration, which can be used to request access tokens with short durations. This enhances security and limits the damage caused if an attacker manages to steal an access token. -- Client Credentials Grant flow, where the client application directly requests an access token using its client ID and secret. There is no user involved. -- Access tokens often have much shorter expiration times compared to user-granted OAuth tokens, since there is no user to re-authenticate. -- The client authentication can use a pre-established client ID and secret (symmetric key), or leverage public/private key pairs (asymmetric keys) for higher security. +The protocol above is called a flow, specifically the authorization code flow. What the flow returns is called a grant — access to the requested scopes. The grant is represented by data like the client credentials and the access token. The word grant is often used interchangeably with the word flow, but that isn't strictly correct. + +For a detailed overview of all OAuth flows, please read the [FusionAuth modern guide to OAuth](https://fusionauth.io/resources/the-modern-guide-to-oauth.pdf). + +## Why Not Use Only A Username And Password For Machine Authorization? + +Let's move on from discussing users to discussing services. When you've called an API, you've most likely used a username and password — which might be called an access token or an API key. This is simple and secure, so why complicate it by using OAuth when you don't need to worry about users anyway? + +Credential Management & Rotation + +- With basic auth, changing passwords requires updating all services using them +- OAuth allows you to rotate client secrets without service downtime +- You can revoke specific tokens without changing the main credentials -## Why Not Use Only A Username And Password? +Fine-grained Access Control -While username/password (basic auth) might seem simpler, using OAuth for service-to-service communication provides several crucial security benefits: +- OAuth scopes let you limit what each service can do +Example: Service A might only need "read" access to users, while Service B - needs "write" access +- Basic auth is usually all-or-nothing access - Credential Management & Rotation +Audit Trail & Monitoring - With basic auth, changing passwords requires updating all services using them - OAuth allows you to rotate client secrets without service downtime - You can revoke specific tokens without changing the main credentials +- OAuth tokens can be tracked individually +You can see which service accessed what and when +- With basic auth, multiple services sharing the same credentials are - indistinguishable - Fine-grained Access Control +Standardized Security - OAuth scopes let you limit what each service can do - Example: Service A might only need "read" access to users, while Service B needs "write" access - Basic auth is usually all-or-nothing access +- OAuth is a well-tested protocol with industry best practices +Many security tools and platforms are built to work with OAuth +- Basic auth lacks features like token expiration and scope control - Audit Trail & Monitoring +Scalability - OAuth tokens can be tracked individually - You can see which service accessed what and when - With basic auth, multiple services sharing the same credentials are indistinguishable +- As your system grows, OAuth makes it easier to manage hundreds of service-to-service connections +- Each service can have its own client credentials with specific permissions +- Basic auth becomes a maintenance nightmare at scale - Standardized Security - OAuth is a well-tested protocol with industry best practices - Many security tools and platforms are built to work with OAuth - Basic auth lacks features like token expiration and scope control - Scalability - As your system grows, OAuth makes it easier to manage hundreds of service-to-service connections - Each service can have its own client credentials with specific permissions - Basic auth becomes a maintenance nightmare at scale +- Client Credentials Grant flow, where the client application directly requests an access token using its client ID and secret. There is no user involved. +- Access tokens often have much shorter expiration times compared to user-granted OAuth tokens, since there is no user to re-authenticate. +- The client authentication can use a pre-established client ID and secret (symmetric key), or leverage public/private key pairs (asymmetric keys) for higher security. + + ## Further Reading -## TODO \ No newline at end of file +- [FusionAuth modern guide to OAuth](https://fusionauth.io/resources/the-modern-guide-to-oauth.pdf) + +## TODO + +OA OAuth +FA FusionAuth + + client credentials grant + how to create/manage scopes + how to think about on behalf of calls + token expiration time + what kind of signing key to use (symmetric? asymmetric?) + +Have this playlist which helps some: https://www.youtube.com/watch?v=rT-VTtgligI&list=PLUOVyt5DYPpNzRdTKrio0P0a4mktqLPN9 From 5d505bd4f27309a5fc97b60d17e945667fdc1449 Mon Sep 17 00:00:00 2001 From: RichardJECooke Date: Mon, 28 Oct 2024 14:47:02 +0200 Subject: [PATCH 03/34] save --- .../articles/authentication/service-to-service.mdx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/astro/src/content/articles/authentication/service-to-service.mdx b/astro/src/content/articles/authentication/service-to-service.mdx index 522ee1958b..bacbb16e73 100644 --- a/astro/src/content/articles/authentication/service-to-service.mdx +++ b/astro/src/content/articles/authentication/service-to-service.mdx @@ -10,6 +10,7 @@ darkIcon: /img/icons/webauthn-explained-dark.svg - [Introduction](#introduction) - [Understand user login](#understand-user-login) +- [Understand machine login](#understand-machine-login) - [Why Not Use Only A Username And Password For Machine Authorization?](#why-not-use-only-a-username-and-password-for-machine-authorization) - [Further Reading](#further-reading) - [TODO](#todo) @@ -23,18 +24,24 @@ This article explains how to use OAuth 2.0 and FusionAuth for service to service Let's start by summarizing how OAuth normally works for user login. You've most likely used FA for local login or third-party login. With local login, FA stores usernames and password hashes. With third-party login, another organization, like Google, handles authentication and FA is merely an intermediary. Either way, the authentication flow works as follows: - A user (resource owner) clicks "Log in" on your website (client). -- Your site redirects the user to the URL of the authorization server, which could either be FA directly, or another redirection to Google. This URL includes parameters for the Id of your site (client Id), the URL the user's browser should be sent to after logging in (redirect URI), the response type of code, and the permissions your site is requesting on behalf of the user (scopes). +- Your site redirects the user to the URL (authorization endpoint) of the authorization server, which could either be FA directly, or another redirection to Google. This URL includes parameters for the Id of your site (client Id), the URL the user's browser should be sent to after logging in (redirect URI), the response type of code, and the permissions your site is requesting on behalf of the user (scopes). - The user logs in and consents to the permissions requested on a page provided by the server. - The authorization server redirects the browser back to your site (using the redirect URI), with a temporary authorization code. -- Your site then starts a separate HTTP call to the server directly. This is more secure than using the browser. Now your site can ask the server for a key (access token) to access the user's resources in future calls. In this call your site sends the temporary authorization code, your client Id, and your client secret. The server might instead return a refresh token with a long duration, which can be used to request access tokens with short durations. This enhances security and limits the damage caused if an attacker manages to steal an access token. +- Your site then starts a separate HTTP call to the server directly (to the token endpoint). This is more secure than using the browser. Now your site can ask the server for a key (access token) to access the user's resources in future calls. In this call your site sends the temporary authorization code, your client Id, and your client secret. The server might instead return a refresh token with a long duration, which can be used to request access tokens with short durations. This enhances security and limits the damage caused if an attacker manages to steal an access token. The protocol above is called a flow, specifically the authorization code flow. What the flow returns is called a grant — access to the requested scopes. The grant is represented by data like the client credentials and the access token. The word grant is often used interchangeably with the word flow, but that isn't strictly correct. For a detailed overview of all OAuth flows, please read the [FusionAuth modern guide to OAuth](https://fusionauth.io/resources/the-modern-guide-to-oauth.pdf). +## Understand machine login + +Let's move on from discussing users to discussing services. The OA flow for calling an API from a machine with no user login page is called the client credentials flow. + +- Your app calls the token endpoint of the authorization server, passing your `client_id`, `client_secret`, and `grant_type` with value `client_credentials`. + ## Why Not Use Only A Username And Password For Machine Authorization? -Let's move on from discussing users to discussing services. When you've called an API, you've most likely used a username and password — which might be called an access token or an API key. This is simple and secure, so why complicate it by using OAuth when you don't need to worry about users anyway? +When you've called an API, you've most likely used a username and password — which might be called an access token or an API key. This is simple and secure, so why complicate it by using OAuth when you don't need to worry about users anyway? Credential Management & Rotation From 8fdd5a5db8be3c556b7a9d7899cc5fad6543804a Mon Sep 17 00:00:00 2001 From: RichardJECooke Date: Mon, 28 Oct 2024 15:32:54 +0200 Subject: [PATCH 04/34] save --- .../content/articles/authentication/service-to-service.mdx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/astro/src/content/articles/authentication/service-to-service.mdx b/astro/src/content/articles/authentication/service-to-service.mdx index bacbb16e73..289afd3d64 100644 --- a/astro/src/content/articles/authentication/service-to-service.mdx +++ b/astro/src/content/articles/authentication/service-to-service.mdx @@ -37,7 +37,9 @@ For a detailed overview of all OAuth flows, please read the [FusionAuth modern g Let's move on from discussing users to discussing services. The OA flow for calling an API from a machine with no user login page is called the client credentials flow. -- Your app calls the token endpoint of the authorization server, passing your `client_id`, `client_secret`, and `grant_type` with value `client_credentials`. +- Your app makes a POST request to the token endpoint of the authorization server, passing your `client_id`, `client_secret`, and `grant_type` with value `client_credentials` as form data. +- Since the app is requesting access to its own resources, not the resources of a user, no consent is required. +- The server will return JSON containing an access token an possibly a list of scopes. ## Why Not Use Only A Username And Password For Machine Authorization? From 7327ba045bb910ecf4159463e4a233211deb7382 Mon Sep 17 00:00:00 2001 From: RichardJECooke Date: Tue, 29 Oct 2024 12:19:57 +0200 Subject: [PATCH 05/34] save todos after watching videos --- .../authentication/service-to-service.mdx | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/astro/src/content/articles/authentication/service-to-service.mdx b/astro/src/content/articles/authentication/service-to-service.mdx index 289afd3d64..48d9eb8323 100644 --- a/astro/src/content/articles/authentication/service-to-service.mdx +++ b/astro/src/content/articles/authentication/service-to-service.mdx @@ -8,9 +8,12 @@ icon: /img/icons/webauthn-explained.svg darkIcon: /img/icons/webauthn-explained-dark.svg --- +import PremiumEditionBlurbApi from 'src/content/docs/_shared/_premium-edition-blurb-api.astro'; + - [Introduction](#introduction) - [Understand user login](#understand-user-login) - [Understand machine login](#understand-machine-login) +- [A FusionAuth Example](#a-fusionauth-example) - [Why Not Use Only A Username And Password For Machine Authorization?](#why-not-use-only-a-username-and-password-for-machine-authorization) - [Further Reading](#further-reading) - [TODO](#todo) @@ -19,6 +22,8 @@ darkIcon: /img/icons/webauthn-explained-dark.svg This article explains how to use OAuth 2.0 and FusionAuth for service to service communication (which you can call machine OA). In other words, how to both provide and call an API programmatically, with no user logging in on a webpage. + + ## Understand user login Let's start by summarizing how OAuth normally works for user login. You've most likely used FA for local login or third-party login. With local login, FA stores usernames and password hashes. With third-party login, another organization, like Google, handles authentication and FA is merely an intermediary. Either way, the authentication flow works as follows: @@ -39,7 +44,26 @@ Let's move on from discussing users to discussing services. The OA flow for call - Your app makes a POST request to the token endpoint of the authorization server, passing your `client_id`, `client_secret`, and `grant_type` with value `client_credentials` as form data. - Since the app is requesting access to its own resources, not the resources of a user, no consent is required. -- The server will return JSON containing an access token an possibly a list of scopes. +- The server will return JSON containing an access token and possibly a list of scopes. + +This flow is much simpler than the user flow earlier, but there are still some design choices to be aware of. Both the client and the resource server have to trust the authorization server. So both parties must be registered users of the authorization server. Additionally, the resource server needs to trust that when any call is made to it with an access token, that that access token represents the permissions granted by a client. So the authorization server must sign the access token (encrypt a hash of the token with its private key). Without a signature, any client could create an access token claiming whatever permissions they wanted. + +## A FusionAuth Example + +todo - read entities in fusionauth docs + +entities, entity types +your app must know what uuids apps are, must check signature +api entity type and customer entity type + +advantages - manage permissions from apps to other apps in fusionauth web interface + +use attributes (cli) and lambdas to manage permissions (roles). or write script to add and remove permissions + + + + + ## Why Not Use Only A Username And Password For Machine Authorization? From 0a323bc92d0f1b5ecd1ff876e786e0e1d5d0195f Mon Sep 17 00:00:00 2001 From: RichardJECooke Date: Thu, 31 Oct 2024 13:00:18 +0200 Subject: [PATCH 06/34] system design --- .../authentication/service-to-service.mdx | 60 ++++++++++++++++--- 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/astro/src/content/articles/authentication/service-to-service.mdx b/astro/src/content/articles/authentication/service-to-service.mdx index 48d9eb8323..e9e3875493 100644 --- a/astro/src/content/articles/authentication/service-to-service.mdx +++ b/astro/src/content/articles/authentication/service-to-service.mdx @@ -13,7 +13,8 @@ import PremiumEditionBlurbApi from 'src/content/docs/_shared/_premium-edition-bl - [Introduction](#introduction) - [Understand user login](#understand-user-login) - [Understand machine login](#understand-machine-login) -- [A FusionAuth Example](#a-fusionauth-example) +- [Designing A FusionAuth Example](#designing-a-fusionauth-example) + - [update customer type - can't](#update-customer-type---cant) - [Why Not Use Only A Username And Password For Machine Authorization?](#why-not-use-only-a-username-and-password-for-machine-authorization) - [Further Reading](#further-reading) - [TODO](#todo) @@ -48,25 +49,70 @@ Let's move on from discussing users to discussing services. The OA flow for call This flow is much simpler than the user flow earlier, but there are still some design choices to be aware of. Both the client and the resource server have to trust the authorization server. So both parties must be registered users of the authorization server. Additionally, the resource server needs to trust that when any call is made to it with an access token, that that access token represents the permissions granted by a client. So the authorization server must sign the access token (encrypt a hash of the token with its private key). Without a signature, any client could create an access token claiming whatever permissions they wanted. -## A FusionAuth Example +## Designing A FusionAuth Example -todo - read entities in fusionauth docs +Imagine you are a backup service that allows customers to save files in your storage. Your free service allows a customer to store up to 100 MB and your premium service allows more than that. You have two customers currently: North University and North Hospital. Customers will authenticate with your API using the client credentials flow. This flow is provided in FusionAuth by Entities, not Users. Please read the [documentation on Entities and Entity Types](https://fusionauth.io/docs/get-started/core-concepts/entity-management) before proceeding. + +Let's look at the implications of this scenario using FusionAuth: + +- Your customers don't have to register on FusionAuth, or have any knowledge of it. It is only your service that needs be aware of FusionAuth, and that the Ids of customers and permissions between FusionAuth and the service database agree. +- When receiving an API call, your service needs to check only that the access token is signed by FusionAuth, that the caller is trying to access its own resorces, and the caller has permissions to perform the requested operation on those resources. +- A customer will change between free and premium. When working with individual users in FusionAuth, you could use roles, but entities do not support roles. How can you model this? + +You can't use entity types for free and premium — one type for customers and one for premium customers — because you cannot change the type of an entity once it is created. Nor can entity types have default permissions to other entity types. Permissions are defined only between two entities. + +Instead, you have two options: +- Write a script that calls the [FusionAuth Entity API](https://fusionauth.io/docs/apis/entities/entities#update-an-entity) and updates all permissions for an entity. Note that you will have to track which permissions premium customers should have and which permissions free customers should have, and which customers are of which type, outside FusionAuth. FusionAuth will have no concept of different customer types. +- Use the entity API to add a custom attribute of `premium` to premium customers. Then write a [Lambda](https://fusionauth.io/docs/extend/code/lambdas/client-credentials-jwt-populate) to check if the customer has the `premium` attribute at login, and add extra permissions to the JWT if it does. This option is more complex than the previous, but allows you to keep all customer type and permission information in one place, in FusionAuth. + +Below is a diagram of the system you'll build in the next section. + +```mermaid + +``` -entities, entity types -your app must know what uuids apps are, must check signature api entity type and customer entity type -advantages - manage permissions from apps to other apps in fusionauth web interface + -use attributes (cli) and lambdas to manage permissions (roles). or write script to add and remove permissions +### update customer type - can't +update type from customer to api +33052c8a-c283-4e96-9d2a-eb1215c69f8f-not-for-prod +40450891-0231-49c4-839b-b2c444f57f9c +NorthUniversity +40450891-0231-49c4-839b-b2c444f57f9c +EmQ3FL-rDqHuESnJCmZacFK3sKQbOKX-gQYnC5pPLio +PUT /api/entity/{entityId} +```sh +curl -X PUT \ + -H "Authorization: 33052c8a-c283-4e96-9d2a-eb1215c69f8f-not-for-prod" \ + -H "Content-Type: application/json" \ + -d '{ + "entity": { + "clientId": "40450891-0231-49c4-839b-b2c444f57f9c", + "clientSecret": "EmQ3FL-rDqHuESnJCmZacFK3sKQbOKX-gQYnC5pPLio", + "type": { + "id": "ba94d545-230b-4e9c-95f9-af87c8e97f80" + }, + "name": "NorthUniversity" + } + }' \ + "http://localhost:9011/api/entity/40450891-0231-49c4-839b-b2c444f57f9c" +``` ## Why Not Use Only A Username And Password For Machine Authorization? +advantages - manage permissions from apps to other apps in fusionauth web interface + + + When you've called an API, you've most likely used a username and password — which might be called an access token or an API key. This is simple and secure, so why complicate it by using OAuth when you don't need to worry about users anyway? Credential Management & Rotation From 573dec13ff3a624f1015fcfba06e21305cfdf95f Mon Sep 17 00:00:00 2001 From: RichardJECooke Date: Fri, 1 Nov 2024 10:25:35 +0200 Subject: [PATCH 07/34] explain why to user oauth for services --- .../authentication/service-to-service.mdx | 84 ++++++------------- 1 file changed, 24 insertions(+), 60 deletions(-) diff --git a/astro/src/content/articles/authentication/service-to-service.mdx b/astro/src/content/articles/authentication/service-to-service.mdx index e9e3875493..a088f6c253 100644 --- a/astro/src/content/articles/authentication/service-to-service.mdx +++ b/astro/src/content/articles/authentication/service-to-service.mdx @@ -14,8 +14,8 @@ import PremiumEditionBlurbApi from 'src/content/docs/_shared/_premium-edition-bl - [Understand user login](#understand-user-login) - [Understand machine login](#understand-machine-login) - [Designing A FusionAuth Example](#designing-a-fusionauth-example) - - [update customer type - can't](#update-customer-type---cant) - [Why Not Use Only A Username And Password For Machine Authorization?](#why-not-use-only-a-username-and-password-for-machine-authorization) +- [curl Command To Update An Entity](#curl-command-to-update-an-entity) - [Further Reading](#further-reading) - [TODO](#todo) @@ -51,33 +51,45 @@ This flow is much simpler than the user flow earlier, but there are still some d ## Designing A FusionAuth Example -Imagine you are a backup service that allows customers to save files in your storage. Your free service allows a customer to store up to 100 MB and your premium service allows more than that. You have two customers currently: North University and North Hospital. Customers will authenticate with your API using the client credentials flow. This flow is provided in FusionAuth by Entities, not Users. Please read the [documentation on Entities and Entity Types](https://fusionauth.io/docs/get-started/core-concepts/entity-management) before proceeding. +Imagine you sell a file backup service that allows customers to save files in your storage. Your free service allows a customer to store up to 1 GB and your premium service allows more than that. You have two customers currently: North University and North Hospital. You want to start using OAuth authentication through FusionAuth. Customers will authenticate with your API using the client credentials flow. This flow is provided in FusionAuth by Entities, not Users. Please read the [documentation on Entities and Entity Types](https://fusionauth.io/docs/get-started/core-concepts/entity-management) before proceeding. -Let's look at the implications of this scenario using FusionAuth: +Let's look at the implications of this scenario: -- Your customers don't have to register on FusionAuth, or have any knowledge of it. It is only your service that needs be aware of FusionAuth, and that the Ids of customers and permissions between FusionAuth and the service database agree. -- When receiving an API call, your service needs to check only that the access token is signed by FusionAuth, that the caller is trying to access its own resorces, and the caller has permissions to perform the requested operation on those resources. +- Your customers don't have to register on FusionAuth, or have any knowledge of it. To them, it is merely a generic OAuth endpoint they call to get an access token. It is only your service that needs to be aware of FusionAuth, and have the Ids of customers and permissions between FusionAuth and the service database agree. +- When receiving an API call, your service needs to check that the access token is signed by FusionAuth, that the caller is trying to access its own resorces, and the caller has permissions to perform the requested operation on those resources. - A customer will change between free and premium. When working with individual users in FusionAuth, you could use roles, but entities do not support roles. How can you model this? You can't use entity types for free and premium — one type for customers and one for premium customers — because you cannot change the type of an entity once it is created. Nor can entity types have default permissions to other entity types. Permissions are defined only between two entities. Instead, you have two options: - Write a script that calls the [FusionAuth Entity API](https://fusionauth.io/docs/apis/entities/entities#update-an-entity) and updates all permissions for an entity. Note that you will have to track which permissions premium customers should have and which permissions free customers should have, and which customers are of which type, outside FusionAuth. FusionAuth will have no concept of different customer types. -- Use the entity API to add a custom attribute of `premium` to premium customers. Then write a [Lambda](https://fusionauth.io/docs/extend/code/lambdas/client-credentials-jwt-populate) to check if the customer has the `premium` attribute at login, and add extra permissions to the JWT if it does. This option is more complex than the previous, but allows you to keep all customer type and permission information in one place, in FusionAuth. +- Use the entity API to add a custom attribute of `premium` to premium customers. Then write a [Lambda](https://fusionauth.io/docs/extend/code/lambdas/client-credentials-jwt-populate) in FusionAuth to check if the customer has the `premium` attribute at login, and if so, add extra permissions to the access token returned to the customer This option is more complex than the previous, but allows you to keep all customer type and permission information in one place, in FusionAuth. -Below is a diagram of the system you'll build in the next section. +Below is a diagram of the system you'll build in this guide, using two entity types: API and Customer. ```mermaid - +graph LR + n1("NorthHospital (Customer)") --> |Send token, store files| n2("FileApi (API)") + n3("NorthUniversity (Customer)") --> |Send token, store files| n2 + n4(FusionAuth) + n1 --> |Get token| n4 + n2 --> |Verify token token|n4 + n3 --> |Get token|n4 ``` -api entity type and customer entity type - -### update customer type - can't +## Why Not Use Only A Username And Password For Machine Authorization? + +Before looking at how to code this example, you might be wondering: why bother with the complexity of OAuth and access tokens, instead of giving customers a username and password? Firstly, calling an endpoint to get an access token, and sending that with API calls instead of the password directly, is only one extra step. So it's not much more work for customers. There are also several advantages: +- You can manage entities and permissions in one place — the FusionAuth web interface. +- Customers can store the client secret (password) very securely, and give only the access token to services that make API calls. In the event this token is exposed to attackers, it can be immediately revoked through an OAuth endpoint, without needing to change the client secret. Since access tokens expire, you also have password rotation by default. +- Basic authentication with a password usually gives an API caller full rights to do anything. With OAuth, you can issue a token with only certain permissions. In FusionAuth, you will need to create multiple entities to do this. For instance, you might create North Hospital Imaging and North Hospital Admissions as different entities with slightly different FileAPI permissions. +- Access tokens can be logged individually. You can see which service accessed what resource and when. With basic authentication, different services sharing the same customer username and password are indistinguishable. + +## curl Command To Update An Entity update type from customer to api @@ -107,54 +119,6 @@ curl -X PUT \ "http://localhost:9011/api/entity/40450891-0231-49c4-839b-b2c444f57f9c" ``` -## Why Not Use Only A Username And Password For Machine Authorization? - -advantages - manage permissions from apps to other apps in fusionauth web interface - - - -When you've called an API, you've most likely used a username and password — which might be called an access token or an API key. This is simple and secure, so why complicate it by using OAuth when you don't need to worry about users anyway? - -Credential Management & Rotation - -- With basic auth, changing passwords requires updating all services using them -- OAuth allows you to rotate client secrets without service downtime -- You can revoke specific tokens without changing the main credentials - -Fine-grained Access Control - -- OAuth scopes let you limit what each service can do -Example: Service A might only need "read" access to users, while Service B - needs "write" access -- Basic auth is usually all-or-nothing access - -Audit Trail & Monitoring - -- OAuth tokens can be tracked individually -You can see which service accessed what and when -- With basic auth, multiple services sharing the same credentials are - indistinguishable - -Standardized Security - -- OAuth is a well-tested protocol with industry best practices -Many security tools and platforms are built to work with OAuth -- Basic auth lacks features like token expiration and scope control - -Scalability - -- As your system grows, OAuth makes it easier to manage hundreds of service-to-service connections -- Each service can have its own client credentials with specific permissions -- Basic auth becomes a maintenance nightmare at scale - - - - -- Client Credentials Grant flow, where the client application directly requests an access token using its client ID and secret. There is no user involved. -- Access tokens often have much shorter expiration times compared to user-granted OAuth tokens, since there is no user to re-authenticate. -- The client authentication can use a pre-established client ID and secret (symmetric key), or leverage public/private key pairs (asymmetric keys) for higher security. - - - - ## Further Reading - [FusionAuth modern guide to OAuth](https://fusionauth.io/resources/the-modern-guide-to-oauth.pdf) From 8c7033e92922df3660224afb0031713d31419035 Mon Sep 17 00:00:00 2001 From: RichardJECooke Date: Fri, 1 Nov 2024 10:32:46 +0200 Subject: [PATCH 08/34] edit --- .../articles/authentication/service-to-service.mdx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/astro/src/content/articles/authentication/service-to-service.mdx b/astro/src/content/articles/authentication/service-to-service.mdx index a088f6c253..5f2ebe338f 100644 --- a/astro/src/content/articles/authentication/service-to-service.mdx +++ b/astro/src/content/articles/authentication/service-to-service.mdx @@ -11,8 +11,8 @@ darkIcon: /img/icons/webauthn-explained-dark.svg import PremiumEditionBlurbApi from 'src/content/docs/_shared/_premium-edition-blurb-api.astro'; - [Introduction](#introduction) -- [Understand user login](#understand-user-login) -- [Understand machine login](#understand-machine-login) +- [Understand OAuth User Login](#understand-oauth-user-login) +- [Understand OAuth Machine Login](#understand-oauth-machine-login) - [Designing A FusionAuth Example](#designing-a-fusionauth-example) - [Why Not Use Only A Username And Password For Machine Authorization?](#why-not-use-only-a-username-and-password-for-machine-authorization) - [curl Command To Update An Entity](#curl-command-to-update-an-entity) @@ -25,12 +25,12 @@ This article explains how to use OAuth 2.0 and FusionAuth for service to service -## Understand user login +## Understand OAuth User Login Let's start by summarizing how OAuth normally works for user login. You've most likely used FA for local login or third-party login. With local login, FA stores usernames and password hashes. With third-party login, another organization, like Google, handles authentication and FA is merely an intermediary. Either way, the authentication flow works as follows: - A user (resource owner) clicks "Log in" on your website (client). -- Your site redirects the user to the URL (authorization endpoint) of the authorization server, which could either be FA directly, or another redirection to Google. This URL includes parameters for the Id of your site (client Id), the URL the user's browser should be sent to after logging in (redirect URI), the response type of code, and the permissions your site is requesting on behalf of the user (scopes). +- Your site redirects the user to the URL (authorization endpoint) of the authorization server, which could either be FA directly, or another redirection to Google. This URL includes parameters for the Id of your site (client Id), the URL the user's browser should be sent to after logging in (redirect URI), the response type of code, and the permissions (scopes) your site is requesting on behalf of the user. - The user logs in and consents to the permissions requested on a page provided by the server. - The authorization server redirects the browser back to your site (using the redirect URI), with a temporary authorization code. - Your site then starts a separate HTTP call to the server directly (to the token endpoint). This is more secure than using the browser. Now your site can ask the server for a key (access token) to access the user's resources in future calls. In this call your site sends the temporary authorization code, your client Id, and your client secret. The server might instead return a refresh token with a long duration, which can be used to request access tokens with short durations. This enhances security and limits the damage caused if an attacker manages to steal an access token. @@ -39,12 +39,12 @@ The protocol above is called a flow, specifically the authorization code flow. W For a detailed overview of all OAuth flows, please read the [FusionAuth modern guide to OAuth](https://fusionauth.io/resources/the-modern-guide-to-oauth.pdf). -## Understand machine login +## Understand OAuth Machine Login -Let's move on from discussing users to discussing services. The OA flow for calling an API from a machine with no user login page is called the client credentials flow. +Let's move on from discussing users to discussing services, or machine-to-machine OAuth. The flow for calling an API from a machine with no user login page is called the client credentials flow. - Your app makes a POST request to the token endpoint of the authorization server, passing your `client_id`, `client_secret`, and `grant_type` with value `client_credentials` as form data. -- Since the app is requesting access to its own resources, not the resources of a user, no consent is required. +- Since the app is requesting access to its own resources, not the resources of another user, no consent is required. - The server will return JSON containing an access token and possibly a list of scopes. This flow is much simpler than the user flow earlier, but there are still some design choices to be aware of. Both the client and the resource server have to trust the authorization server. So both parties must be registered users of the authorization server. Additionally, the resource server needs to trust that when any call is made to it with an access token, that that access token represents the permissions granted by a client. So the authorization server must sign the access token (encrypt a hash of the token with its private key). Without a signature, any client could create an access token claiming whatever permissions they wanted. From 9e82d13c665e382acea47673006c4d8c6e9767d5 Mon Sep 17 00:00:00 2001 From: RichardJECooke Date: Fri, 1 Nov 2024 10:41:20 +0200 Subject: [PATCH 09/34] added code plan --- .../authentication/service-to-service.mdx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/astro/src/content/articles/authentication/service-to-service.mdx b/astro/src/content/articles/authentication/service-to-service.mdx index 5f2ebe338f..62a703551c 100644 --- a/astro/src/content/articles/authentication/service-to-service.mdx +++ b/astro/src/content/articles/authentication/service-to-service.mdx @@ -16,6 +16,7 @@ import PremiumEditionBlurbApi from 'src/content/docs/_shared/_premium-edition-bl - [Designing A FusionAuth Example](#designing-a-fusionauth-example) - [Why Not Use Only A Username And Password For Machine Authorization?](#why-not-use-only-a-username-and-password-for-machine-authorization) - [curl Command To Update An Entity](#curl-command-to-update-an-entity) +- [Set Up A FusionAuth Machine-To-Machine OAuth Example](#set-up-a-fusionauth-machine-to-machine-oauth-example) - [Further Reading](#further-reading) - [TODO](#todo) @@ -119,6 +120,22 @@ curl -X PUT \ "http://localhost:9011/api/entity/40450891-0231-49c4-839b-b2c444f57f9c" ``` +## Set Up A FusionAuth Machine-To-Machine OAuth Example + +- create entity types API and Customer +- create entities FileAPI NorthHospital and NorthUniversity +- create customer.js + - create js to call api and get access token + - see token on jwt.io and fusionauth equivalent website, check if can verify token against local host +- create server.js + - use express to receive a single file storage call + - create js to verify token in api + - unwrap token to see claims +- create curl script to update customer permissions +- create curl script to add or remove `premium` attribute + - create lambda to change access token permissions based on the attribute + + ## Further Reading - [FusionAuth modern guide to OAuth](https://fusionauth.io/resources/the-modern-guide-to-oauth.pdf) From d6a1615ae52e9519731d1719fb89fec8a3d2ae3c Mon Sep 17 00:00:00 2001 From: RichardJECooke Date: Fri, 1 Nov 2024 12:02:35 +0200 Subject: [PATCH 10/34] save entity setup in fusionauth --- .../service-to-service/fileapi.webp | Bin 0 -> 26510 bytes .../service-to-service/types.webp | Bin 0 -> 29070 bytes .../authentication/service-to-service.mdx | 93 +++++++++++++++--- 3 files changed, 77 insertions(+), 16 deletions(-) create mode 100644 astro/public/img/articles/authentication/service-to-service/fileapi.webp create mode 100644 astro/public/img/articles/authentication/service-to-service/types.webp diff --git a/astro/public/img/articles/authentication/service-to-service/fileapi.webp b/astro/public/img/articles/authentication/service-to-service/fileapi.webp new file mode 100644 index 0000000000000000000000000000000000000000..7a883bca2e35e602bc4a2fbdc8b850e0deb14ab4 GIT binary patch literal 26510 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 wfe}S}3W@Ub=vxtL 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 Date: Fri, 1 Nov 2024 12:52:37 +0200 Subject: [PATCH 11/34] add service permissions --- .../articles/authentication/service-to-service.mdx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/astro/src/content/articles/authentication/service-to-service.mdx b/astro/src/content/articles/authentication/service-to-service.mdx index cc8b23e954..a6120a4e84 100644 --- a/astro/src/content/articles/authentication/service-to-service.mdx +++ b/astro/src/content/articles/authentication/service-to-service.mdx @@ -18,6 +18,7 @@ import PremiumEditionBlurbApi from 'src/content/docs/_shared/_premium-edition-bl - [Set Up A FusionAuth Machine-To-Machine OAuth Example](#set-up-a-fusionauth-machine-to-machine-oauth-example) - [Create Entity Types](#create-entity-types) - [Create Entities](#create-entities) + - [Add Permissions To Customers](#add-permissions-to-customers) - [Create Customer Code](#create-customer-code) - [curl Command To Update An Entity](#curl-command-to-update-an-entity) - [Further Reading](#further-reading) @@ -148,9 +149,20 @@ Note that the client Ids and FusionAuth Ids are the same for each entity by defa You are not saying anything about premium customers yet, except to have the `StoreLargeData` attribute in the API type. +### Add Permissions To Customers + +- In the Entities screen, click `Manage` for `NorthUniversity`. +- In the `Entity grants` tab at the bottom, click `Add`. +- Type `api` in the search box and select `FileApi`. +- Enable all permissions except `StoreLargeData` and click `Save`. + +The university now has permissions to add and update files through the API. + ### Create Customer Code -You are now ready to get access tokens from FusionAuth as a Customer, and verify tokens as the API. +You are ready to get access tokens from FusionAuth as a Customer, and verify tokens as the API. + +- Create a file called `customer.mjs`. From cffbd03ba9933587b46755a001c041fd21a4bbb6 Mon Sep 17 00:00:00 2001 From: RichardJECooke Date: Fri, 1 Nov 2024 13:59:13 +0200 Subject: [PATCH 12/34] got access token --- .../service-to-service/jwt.webp | Bin 0 -> 65384 bytes .../authentication/service-to-service.mdx | 58 ++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 astro/public/img/articles/authentication/service-to-service/jwt.webp diff --git a/astro/public/img/articles/authentication/service-to-service/jwt.webp b/astro/public/img/articles/authentication/service-to-service/jwt.webp new file mode 100644 index 0000000000000000000000000000000000000000..45c48de59f09b6c5df6c01b17c0c9c3f87f602e2 GIT binary patch literal 65384 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 literal 0 HcmV?d00001 diff --git a/astro/src/content/articles/authentication/service-to-service.mdx b/astro/src/content/articles/authentication/service-to-service.mdx index a6120a4e84..d5bf6858bd 100644 --- a/astro/src/content/articles/authentication/service-to-service.mdx +++ b/astro/src/content/articles/authentication/service-to-service.mdx @@ -163,6 +163,64 @@ The university now has permissions to add and update files through the API. You are ready to get access tokens from FusionAuth as a Customer, and verify tokens as the API. - Create a file called `customer.mjs`. +- Add the code below to get the access token for NorthUniversity. + ```js + import axios from "axios"; + + async function getAccessToken() { + try { + const formData = new URLSearchParams(); + formData.append("grant_type", "client_credentials"); + formData.append("client_id", "40450891-0231-49c4-839b-b2c444f57f9c"); + formData.append("client_secret", "EmQ3FL-rDqHuESnJCmZacFK3sKQbOKX-gQYnC5pPLio"); + formData.append("scope", "read create update"); + + const response = await axios({ + method: "post", + url: "http://localhost:9011/oauth2/token", + headers: { "Content-Type": "application/x-www-form-urlencoded"}, + data: formData, + }); + + console.log("Access Token:", response.data.access_token); + return response.data; + } catch (error) { + console.error("Error getting access token:", error.response?.data || error.message); + throw error; + } + } + + await getAccessToken(); + ``` +- Run the command below in a terminal to make the JavaScript above get a token. + ```sh + docker run --rm --network host -v ".:/app" -w "/app" node:23-alpine3.19 sh -c \ + "npm install axios && node customer.mjs" + + # Output + # Access Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImd0eSI6WyJjbGllbnRfY3JlZGVudGlhbHMiXSwia2lkIjoiOGE3NWQ3YzkzIn0.eyJleHAiOjE3MzA0NjQ1MzIsImlhdCI6MTczMDQ2MDkzMiwiaXNzIjoiYWNtZS5jb20iLCJzdWIiOiI0MDQ1MDg5MS0wMjMxLTQ5YzQtODM5Yi1iMmM0NDRmNTdmOWMiLCJqdGkiOiI0ZWRkOGRjMy04YjAwLTQ2YTktOWZhNy1lNTY2YmE2ZGU4ZDUiLCJ0aWQiOiJkN2QwOTUxMy1hM2Y1LTQwMWMtOTY4NS0zNGFiNmM1NTI0NTMifQ.a46p7gVAHKOM4yPIPrvc_WzEd7AdToWPwR0Xguaoxyg + ``` +- Paste the token from your terminal into the textbox on the page at https://fusionauth.io/dev-tools/jwt-decoder. + ![Decoded JWT](../../../../public/img/articles/authentication/service-to-service/jwt.webp) +- Below is what each of the fields means: + ```js + { + "alg": "HS256", // Algorithm used for signing the token (HMAC SHA-256) + "typ": "JWT", // Type of token (JSON Web Token) + "gty": [ // Grant type used to obtain this token + "client_credentials" + ], + "kid": "8a75d7c93" // Key ID - identifies which key was used to sign this token + } + { + "exp": 1730464532, // Expiration time (Unix timestamp when token expires) + "iat": 1730460932, // Issued At (Unix timestamp when token was issued) + "iss": "acme.com", // Issuer (who created and signed this token) + "sub": "40450891-0231-49c4-839b-b2c444f57f9c", // Subject (whom the token refers to, in this case the client ID) + "jti": "4edd8dc3-8b00-46a9-9fa7-e566ba6de8d5", // JWT ID (unique identifier for this token) + "tid": "d7d09513-a3f5-401c-9685-34ab6c552453" // Tenant ID (if using multi-tenancy) + } + ``` From 3d0929d57194b2129ffa8b3083a5e47b9351dd06 Mon Sep 17 00:00:00 2001 From: RichardJECooke Date: Fri, 1 Nov 2024 14:11:38 +0200 Subject: [PATCH 13/34] save - authenticated token --- .../authentication/service-to-service.mdx | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/astro/src/content/articles/authentication/service-to-service.mdx b/astro/src/content/articles/authentication/service-to-service.mdx index d5bf6858bd..bf460f5d1f 100644 --- a/astro/src/content/articles/authentication/service-to-service.mdx +++ b/astro/src/content/articles/authentication/service-to-service.mdx @@ -19,7 +19,8 @@ import PremiumEditionBlurbApi from 'src/content/docs/_shared/_premium-edition-bl - [Create Entity Types](#create-entity-types) - [Create Entities](#create-entities) - [Add Permissions To Customers](#add-permissions-to-customers) - - [Create Customer Code](#create-customer-code) + - [Create Customer Code To Get Access Token](#create-customer-code-to-get-access-token) + - [Authenticate The Access Token](#authenticate-the-access-token) - [curl Command To Update An Entity](#curl-command-to-update-an-entity) - [Further Reading](#further-reading) - [TODO](#todo) @@ -158,7 +159,7 @@ You are not saying anything about premium customers yet, except to have the `Sto The university now has permissions to add and update files through the API. -### Create Customer Code +### Create Customer Code To Get Access Token You are ready to get access tokens from FusionAuth as a Customer, and verify tokens as the API. @@ -183,7 +184,7 @@ You are ready to get access tokens from FusionAuth as a Customer, and verify tok }); console.log("Access Token:", response.data.access_token); - return response.data; + return response.data.access_token; } catch (error) { console.error("Error getting access token:", error.response?.data || error.message); throw error; @@ -222,6 +223,25 @@ You are ready to get access tokens from FusionAuth as a Customer, and verify tok } ``` +TODO make a call to the API with this token + +### Authenticate The Access Token + +The API needs to authenticate the access token is really from FusionAuth, rather than being sent by an attacker. + + + + ```js + Token verification result: { + active: true, + exp: 1730466482, + iat: 1730462882, + iss: 'acme.com', + jti: '7cc7151c-aa32-4d4b-b602-072a8584f052', + sub: '40450891-0231-49c4-839b-b2c444f57f9c', + tid: 'd7d09513-a3f5-401c-9685-34ab6c552453' + } + ``` - create customer.js From 441ed0fbf006547e28b3b3534789d149ad4a1e9a Mon Sep 17 00:00:00 2001 From: RichardJECooke Date: Mon, 4 Nov 2024 13:02:23 +0200 Subject: [PATCH 14/34] explain signing keys --- .../authentication/service-to-service.mdx | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/astro/src/content/articles/authentication/service-to-service.mdx b/astro/src/content/articles/authentication/service-to-service.mdx index bf460f5d1f..a8ef087a3a 100644 --- a/astro/src/content/articles/authentication/service-to-service.mdx +++ b/astro/src/content/articles/authentication/service-to-service.mdx @@ -106,14 +106,20 @@ TODO fusionauth light docker compose - In the sidebar, open `Entity Management - Entity Types`. - Click the `Add` button to create a new type. - Enter `Customer` for `Name`. (You don't need to enter a specific UUID, because you won't be altering entity types in scripts.) +- In the JWT tab, enabled `Enabled` and set the `Access token signing key` to `RS256`. - Click `Save` at the top right. - You have just created a new Entity Type, Customer, that will be the type of all your customers using your API. - Add a new type with the name `API`. +- You don't need to change the JWT signing algorithm here, because the API doesn't ever create access tokens - it only verifies tokens from clients. - This time, before saving, add the following permissions in the permissions tab: `Create`, `Read`, `Update`, `StoreLargeData`. - Save. ![Entity types in FusionAuth](../../../../public/img/articles/authentication/service-to-service/types.webp) + + ### Create Entities - In the sidebar, open `Entity Management - Entites`. @@ -195,8 +201,10 @@ You are ready to get access tokens from FusionAuth as a Customer, and verify tok ``` - Run the command below in a terminal to make the JavaScript above get a token. ```sh + docker run --rm -v ".:/app" -w "/app" node:23-alpine3.19 sh -c \ + "npm install axios jsonwebtoken jwks-rsa" docker run --rm --network host -v ".:/app" -w "/app" node:23-alpine3.19 sh -c \ - "npm install axios && node customer.mjs" + "node customer.mjs" # Output # Access Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImd0eSI6WyJjbGllbnRfY3JlZGVudGlhbHMiXSwia2lkIjoiOGE3NWQ3YzkzIn0.eyJleHAiOjE3MzA0NjQ1MzIsImlhdCI6MTczMDQ2MDkzMiwiaXNzIjoiYWNtZS5jb20iLCJzdWIiOiI0MDQ1MDg5MS0wMjMxLTQ5YzQtODM5Yi1iMmM0NDRmNTdmOWMiLCJqdGkiOiI0ZWRkOGRjMy04YjAwLTQ2YTktOWZhNy1lNTY2YmE2ZGU4ZDUiLCJ0aWQiOiJkN2QwOTUxMy1hM2Y1LTQwMWMtOTY4NS0zNGFiNmM1NTI0NTMifQ.a46p7gVAHKOM4yPIPrvc_WzEd7AdToWPwR0Xguaoxyg @@ -223,8 +231,6 @@ You are ready to get access tokens from FusionAuth as a Customer, and verify tok } ``` -TODO make a call to the API with this token - ### Authenticate The Access Token The API needs to authenticate the access token is really from FusionAuth, rather than being sent by an attacker. @@ -243,13 +249,10 @@ The API needs to authenticate the access token is really from FusionAuth, rather } ``` - -- create customer.js - - create js to call api and get access token - - see token on jwt.io and fusionauth equivalent website, check if can verify token against local host - create server.js - use express to receive a single file storage call - - create js to verify token in api + - create js to verify token in api - FIX error that won't allow you to use server client id to call the introspect endpoint + - - unwrap token to see claims - create curl script to update customer permissions - create curl script to add or remove `premium` attribute @@ -290,6 +293,8 @@ curl -X PUT \ ## Further Reading - [FusionAuth modern guide to OAuth](https://fusionauth.io/resources/the-modern-guide-to-oauth.pdf) +- https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/endpoints +- https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/endpoints#introspect ## TODO From c337162df5c36e4f8a0517c4d0e460b5f69abace Mon Sep 17 00:00:00 2001 From: RichardJECooke Date: Mon, 4 Nov 2024 13:05:56 +0200 Subject: [PATCH 15/34] save todo --- .../content/articles/authentication/service-to-service.mdx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/astro/src/content/articles/authentication/service-to-service.mdx b/astro/src/content/articles/authentication/service-to-service.mdx index a8ef087a3a..c5b35e6e63 100644 --- a/astro/src/content/articles/authentication/service-to-service.mdx +++ b/astro/src/content/articles/authentication/service-to-service.mdx @@ -249,11 +249,6 @@ The API needs to authenticate the access token is really from FusionAuth, rather } ``` -- create server.js - - use express to receive a single file storage call - - create js to verify token in api - FIX error that won't allow you to use server client id to call the introspect endpoint - - - - unwrap token to see claims - create curl script to update customer permissions - create curl script to add or remove `premium` attribute - create lambda to change access token permissions based on the attribute @@ -301,6 +296,8 @@ curl -X PUT \ OA OAuth FA FusionAuth +FIX error that won't allow you to use server client id to call the introspect endpoint + client credentials grant how to create/manage scopes how to think about on behalf of calls From c717a564ca761dc75a87a327abe5ef6b2d459602 Mon Sep 17 00:00:00 2001 From: RichardJECooke Date: Mon, 4 Nov 2024 16:09:00 +0200 Subject: [PATCH 16/34] update entity script --- .../authentication/service-to-service.mdx | 81 ++++++++++++++----- 1 file changed, 60 insertions(+), 21 deletions(-) diff --git a/astro/src/content/articles/authentication/service-to-service.mdx b/astro/src/content/articles/authentication/service-to-service.mdx index c5b35e6e63..7d2f6b830e 100644 --- a/astro/src/content/articles/authentication/service-to-service.mdx +++ b/astro/src/content/articles/authentication/service-to-service.mdx @@ -22,6 +22,8 @@ import PremiumEditionBlurbApi from 'src/content/docs/_shared/_premium-edition-bl - [Create Customer Code To Get Access Token](#create-customer-code-to-get-access-token) - [Authenticate The Access Token](#authenticate-the-access-token) - [curl Command To Update An Entity](#curl-command-to-update-an-entity) +- [Check The Updated Permissions](#check-the-updated-permissions) +- [Write A Lambda Function To Add The StoreLargeData Permission To Premium Customers](#write-a-lambda-function-to-add-the-storelargedata-permission-to-premium-customers) - [Further Reading](#further-reading) - [TODO](#todo) @@ -249,41 +251,78 @@ The API needs to authenticate the access token is really from FusionAuth, rather } ``` -- create curl script to update customer permissions -- create curl script to add or remove `premium` attribute - - create lambda to change access token permissions based on the attribute - - ## curl Command To Update An Entity -update type from customer to api +Let's run a script to remove the `Read` permission for the API from North University and give it the premium functionality of `StoreLargeData`. Removing the read permission will allow you to test if access tokens really work. The documentation for the grants (permissions) API is [here](https://fusionauth.io/docs/apis/entities/grants#grant-a-user-or-entity-permissions-to-an-entity). -33052c8a-c283-4e96-9d2a-eb1215c69f8f-not-for-prod - -40450891-0231-49c4-839b-b2c444f57f9c -NorthUniversity -40450891-0231-49c4-839b-b2c444f57f9c -EmQ3FL-rDqHuESnJCmZacFK3sKQbOKX-gQYnC5pPLio - -PUT /api/entity/{entityId} +For all APIs, you would expect that the URL refers to the object you are updating, such as `http://localhost:9011/api/entity/09a00bb3-5099-4eff-af10-a1c139e847f9` updating the FileAPI in the code below. But in this FusionAuth API, it's backwards — the Id in the URL is where the permissions point to, and the Id of the object that's actually being updated is inside the JSON, `"recipientEntityId": "40450891-0231-49c4-839b-b2c444f57f9c" `. ```sh curl -X PUT \ -H "Authorization: 33052c8a-c283-4e96-9d2a-eb1215c69f8f-not-for-prod" \ -H "Content-Type: application/json" \ -d '{ - "entity": { - "clientId": "40450891-0231-49c4-839b-b2c444f57f9c", - "clientSecret": "EmQ3FL-rDqHuESnJCmZacFK3sKQbOKX-gQYnC5pPLio", - "type": { - "id": "ba94d545-230b-4e9c-95f9-af87c8e97f80" - }, - "name": "NorthUniversity" + "grant": { + "permissions": [ + "Create", + "Update", + "StoreLargeData" + ], + "recipientEntityId": "40450891-0231-49c4-839b-b2c444f57f9c" } }' \ + "http://localhost:9011/api/entity/09a00bb3-5099-4eff-af10-a1c139e847f9/grant" +``` + +The script above is a manual way to change a customer's permissions from free to premium. + +Next you're going to run another script to add the `premium` attribute to the North University entity. This allows you to manually add permissions to the access token in a lambda, as discussed earlier. This API is documented [here](https://fusionauth.io/docs/apis/entities/entities#update-an-entity). + +```sh +curl -X PATCH \ + -H "Authorization: 33052c8a-c283-4e96-9d2a-eb1215c69f8f-not-for-prod" \ + -H "Content-Type: application/json" \ + -d '{ + "entity": { "data": { "premium": true } } + }' \ "http://localhost:9011/api/entity/40450891-0231-49c4-839b-b2c444f57f9c" ``` +If you view North University in the FusionAuth web interface entities screen, you'll see it now has updated data and permissions. + +## Check The Updated Permissions + +Run the customer script from before and you will now see it immediately fail, as expected, because the university requests read permissions to the file API, but they were removed in FusionAuth. + +```sh +docker run --rm --network host -v ".:/app" -w "/app" node:23-alpine3.19 sh -c \ + "node customer.mjs" + +# Output: error_description: 'Invalid target-entity scope. The permission names [Read] are invalid.', +``` + +In `customer.js`, replace the request for the read permission, `"target-entity:09a00bb3-5099-4eff-af10-a1c139e847f9:Read",` with the large data permission, `"target-entity:09a00bb3-5099-4eff-af10-a1c139e847f9:StoreLargeData",`, and rerun the code. It should run successfully again, and in the decoded token you can see the access token now have premium permissions. + +## Write A Lambda Function To Add The StoreLargeData Permission To Premium Customers + +TODO put this in fusionauth web interface and test + +```js +function populate(jwt, recipientEntity, targetEntities, permissions) { + if (recipientEntity.data && recipientEntity.data.premiumAccount) { + jwt.premiumAccount = true; + if (typeof targetEntities === "object") { + var targetId = Object.keys(targetEntities)+""; //convert to string + jwt.permissions[targetId].push("StoreLargeData"); + } + } +} +``` + +- create curl script to update customer permissions +- create curl script to add or remove `premium` attribute + - create lambda to change access token permissions based on the attribute + ## Further Reading From eea5c486499a05050ee888617f41130c8c2603ed Mon Sep 17 00:00:00 2001 From: RichardJECooke Date: Tue, 5 Nov 2024 11:43:47 +0200 Subject: [PATCH 17/34] explain how to create lambda --- .../authentication/service-to-service.mdx | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/astro/src/content/articles/authentication/service-to-service.mdx b/astro/src/content/articles/authentication/service-to-service.mdx index 7d2f6b830e..d9b2b88446 100644 --- a/astro/src/content/articles/authentication/service-to-service.mdx +++ b/astro/src/content/articles/authentication/service-to-service.mdx @@ -305,24 +305,28 @@ In `customer.js`, replace the request for the read permission, `"target-entity:0 ## Write A Lambda Function To Add The StoreLargeData Permission To Premium Customers -TODO put this in fusionauth web interface and test - -```js -function populate(jwt, recipientEntity, targetEntities, permissions) { - if (recipientEntity.data && recipientEntity.data.premiumAccount) { - jwt.premiumAccount = true; - if (typeof targetEntities === "object") { - var targetId = Object.keys(targetEntities)+""; //convert to string - jwt.permissions[targetId].push("StoreLargeData"); +- In the FA web interface sidebar, browse to Customizations -> Lambdas. +- Click the + button at the top right. +- For name, enter `PremiumPermissions`. +- For type, choose `Client credentials JWT populate` +- Enter the code below and save the new lambda. + ```js + function populate(jwt, recipientEntity, targetEntities, permissions) { + if (recipientEntity.data && recipientEntity.data.premium) { + if (typeof targetEntities === "object") { + var targetId = Object.keys(targetEntities)+""; //convert to string + jwt.permissions[targetId].push("StoreLargeData"); + } } } -} -``` - -- create curl script to update customer permissions -- create curl script to add or remove `premium` attribute - - create lambda to change access token permissions based on the attribute + ``` +- Browse to Tenants in the sidebar. +- Click the Edit button for the default tenant. +- Select the OAuth tab. +- For Client credentials populate lambda, select PremiumPermissions lambda you just created. +- Click Save at the top right. +Now FusionAuth will run your custom code whenever returning an access token to a client. If the entity has the attribute `premium`, which you added earlier, then the client will be given the `StoreLargeData` permission to the entity it is requesting permission for. ## Further Reading From 34baf78b791f57b734c03f8a65e54d4d93c65786 Mon Sep 17 00:00:00 2001 From: RichardJECooke Date: Tue, 5 Nov 2024 13:04:40 +0200 Subject: [PATCH 18/34] everything works --- .../authentication/service-to-service.mdx | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/astro/src/content/articles/authentication/service-to-service.mdx b/astro/src/content/articles/authentication/service-to-service.mdx index d9b2b88446..0876d34e17 100644 --- a/astro/src/content/articles/authentication/service-to-service.mdx +++ b/astro/src/content/articles/authentication/service-to-service.mdx @@ -24,6 +24,7 @@ import PremiumEditionBlurbApi from 'src/content/docs/_shared/_premium-edition-bl - [curl Command To Update An Entity](#curl-command-to-update-an-entity) - [Check The Updated Permissions](#check-the-updated-permissions) - [Write A Lambda Function To Add The StoreLargeData Permission To Premium Customers](#write-a-lambda-function-to-add-the-storelargedata-permission-to-premium-customers) +- [Test The Lambda Code](#test-the-lambda-code) - [Further Reading](#further-reading) - [TODO](#todo) @@ -315,7 +316,7 @@ In `customer.js`, replace the request for the read permission, `"target-entity:0 if (recipientEntity.data && recipientEntity.data.premium) { if (typeof targetEntities === "object") { var targetId = Object.keys(targetEntities)+""; //convert to string - jwt.permissions[targetId].push("StoreLargeData"); + jwt.scope += ' target-entity:' + targetId + ':StoreLargeData'; } } } @@ -328,6 +329,41 @@ In `customer.js`, replace the request for the read permission, `"target-entity:0 Now FusionAuth will run your custom code whenever returning an access token to a client. If the entity has the attribute `premium`, which you added earlier, then the client will be given the `StoreLargeData` permission to the entity it is requesting permission for. +## Test The Lambda Code + +Before testing that the lambda populates `StoreLargeData` for North University, you first need to remove the permission you added earlier using the script. Return to the entities screen in FusionAuth and remove the permission there. + +In `customer.mjs`, you also need to remove `target-entity:09a00bb3-5099-4eff-af10-a1c139e847f9:StoreLargeData`, because that permission is no longer explicitly set for the entity. If you try to request it from FusionAuth, you will receive an error. Instead, the lambda function will now automatically append the large data permission to any premium customers. + +Run the customer script in the terminal again. Note below the permission added the end of the `scope` line by the lambda. + +```sh +docker run --rm --network host -v ".:/app" -w "/app" node:23-alpine3.19 sh -c \ + "node customer.mjs" + +# Output: + +Access Token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImd0eSI6WyJjbGllbnRfY3JlZGVudGlhbHMiXSwia2lkIjoibnFSODVDNG9YTHpPX05vNnBDTEc4VEE3dW1ZIn0.eyJhdWQiOiIwOWEwMGJiMy01MDk5LTRlZmYtYWYxMC1hMWMxMzllODQ3ZjkiLCJleHAiOjE3MzA4MDI5NjMsImlhdCI6MTczMDgwMjkwMywiaXNzIjoiYWNtZS5jb20iLCJzdWIiOiI0MDQ1MDg5MS0wMjMxLTQ5YzQtODM5Yi1iMmM0NDRmNTdmOWMiLCJqdGkiOiI2MzRmOGVkNy00Mjg1LTRkODktOTIyYS03OTZiYjExNDVmNzciLCJzY29wZSI6InRhcmdldC1lbnRpdHk6MDlhMDBiYjMtNTA5OS00ZWZmLWFmMTAtYTFjMTM5ZTg0N2Y5OkNyZWF0ZSB0YXJnZXQtZW50aXR5OjA5YTAwYmIzLTUwOTktNGVmZi1hZjEwLWExYzEzOWU4NDdmOTpVcGRhdGUgdGFyZ2V0LWVudGl0eTowOWEwMGJiMy01MDk5LTRlZmYtYWYxMC1hMWMxMzllODQ3Zjk6U3RvcmVMYXJnZURhdGEiLCJ0aWQiOiJkN2QwOTUxMy1hM2Y1LTQwMWMtOTY4NS0zNGFiNmM1NTI0NTMiLCJwZXJtaXNzaW9ucyI6eyIwOWEwMGJiMy01MDk5LTRlZmYtYWYxMC1hMWMxMzllODQ3ZjkiOlsiVXBkYXRlIl19fQ.OKVjv5ygZX5wYjilkStco3QClc4zdOyObFu5NPfMonZsfAoMnWaIQbOvy7NN30P5YpRfJCfY5TWS4vwUJKbRZum8xnihkj7lIMYLfWt6lCbEIEH08PVU_X1MxKD1nlsDJNsvuqPzyhpFXUYNRXtlOJyGc7D-qD4tQ_vjMjlhfIL0fru1m7yte7RO7FUMHCd3vbyU8ZbG8JL74ahpijkGpWcusO96ouYbWAnvDYJu_2OHDbI5Yo1CPtQW9dNELbBXzjk4vFe4BCp46LsEH9m2lc-yj82EJ-ICr6LJ_Gi17sFD_slLAYREYP8bGTOD6Hu_kcuPWoZsrmKU515CsGaI0A +{ + aud: '09a00bb3-5099-4eff-af10-a1c139e847f9', + exp: 1730802963, + iat: 1730802903, + iss: 'acme.com', + sub: '40450891-0231-49c4-839b-b2c444f57f9c', + jti: '634f8ed7-4285-4d89-922a-796bb1145f77', + scope: 'target-entity:09a00bb3-5099-4eff-af10-a1c139e847f9:Create target-entity:09a00bb3-5099-4eff-af10-a1c139e847f9:Update target-entity:09a00bb3-5099-4eff-af10-a1c139e847f9:StoreLargeData', + tid: 'd7d09513-a3f5-401c-9685-34ab6c552453', + permissions: { '09a00bb3-5099-4eff-af10-a1c139e847f9': [ 'Update' ] }, + active: true +} + +xray.jpg stored successfully +``` + +