Skip to content

Commit 15f5ee9

Browse files
docs(authentication): clarify OAuth authStrategies security (#3698)
Document that OAuth providers belong under authentication.oauth and must not be listed in public authStrategies for browser redirect SSO. Rewrite flow #2 guidance so provider-token login requires a verifying getProfile, and cross-link guides and cookbooks.
1 parent e02a580 commit 15f5ee9

9 files changed

Lines changed: 89 additions & 14 deletions

File tree

docs/api/authentication/oauth.md

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,55 @@ The following settings for `app.configure(oauth())` are available:
5656
- `expressSession` - An Express middleware for handling sessions. By default will use an HTTP cookie that is only available for the oAuth flow. **This normally does not need to be changed.**
5757
- `koaSession` - A Koa middleware for handling sessions. By default will use an HTTP cookie that is only available for the oAuth flow. **This normally does not need to be changed.**
5858

59+
### Configuration and security
60+
61+
OAuth setup uses three separate pieces. Only the first two are required for the usual browser redirect login:
62+
63+
| Piece | Role |
64+
| --- | --- |
65+
| `authentication.register('google', new OAuthStrategy())` | Registers the strategy so the OAuth callback can run it |
66+
| `authentication.oauth.google` in configuration | Grant provider options (`key`, `secret`, `scope`, …) |
67+
| `authentication.authStrategies` | Strategy names clients may use on **external** `POST /authentication` |
68+
69+
Browser redirect SSO (`/oauth/<provider>`) only needs **register** + **`authentication.oauth`**. The OAuth callback allows that provider for the internal authentication call after Grant finishes. Provider names do **not** need to be listed in public [`authStrategies`](./service.md#configuration).
70+
71+
The [Feathers generator](../../guides/cli/authentication.md) already follows this pattern: OAuth providers are configured under `authentication.oauth`, while `authStrategies` typically stays `["jwt", "local"]`.
72+
73+
```json
74+
// Typical safe config for browser-only OAuth (matches the generator)
75+
{
76+
"authentication": {
77+
"authStrategies": ["jwt", "local"],
78+
"oauth": {
79+
"google": {
80+
"key": "<Client ID>",
81+
"secret": "<Client secret>"
82+
}
83+
}
84+
}
85+
}
86+
```
87+
88+
```json
89+
// Unsafe for the default OAuthStrategy when you only need browser SSO.
90+
// Do not list provider names here unless you implement verified token login (flow #2).
91+
{
92+
"authentication": {
93+
"authStrategies": ["jwt", "local", "google", "microsoft"]
94+
}
95+
}
96+
```
97+
98+
<BlockQuote type="warning" label="Important">
99+
100+
Putting an OAuth provider name (for example `google` or `github`) in [`authStrategies`](./service.md#configuration) exposes that strategy on external `POST /authentication`.
101+
102+
The default [`getProfile`](#getprofile-data-params) implementation returns `data.profile` from the authentication payload. That is safe when the payload is built **server-side** by the OAuth callback after Grant. It is **not** safe to accept a client-supplied `profile` (for example `{ strategy: 'google', profile: { sub: '...' } }`) as proof of identity. A provider `sub` or `id` is an identifier, not a credential.
103+
104+
Only add a provider to `authStrategies` when you intentionally support [flow #2](#flow) (existing provider access token) **and** override `getProfile` to verify that token with the provider. See the [Facebook](../../cookbook/authentication/facebook.md) and [Firebase](../../cookbook/authentication/firebase.md) cookbooks for verified-token patterns.
105+
106+
</BlockQuote>
107+
59108
### Providers
60109

61110
For specific OAuth provider setup see the following [cookbook](../../cookbook/) guides:
@@ -73,22 +122,29 @@ There are two ways to initiate OAuth authentication:
73122
- User clicks on link to OAuth URL (`oauth/<provider>`)
74123
- Gets redirected to provider and authorizes the application
75124
- Callback to the [OauthStrategy](#oauthstrategy) which
76-
- Gets the users profile
125+
- Gets the users profile (from the server-side Grant response)
77126
- Finds or creates the user (entity) for that profile
78127
- The [AuthenticationService](./service.md) creates an access token for that entity
79128
- Redirects back to the origin URL including the generated access token
80129
- The frontend (e.g. the Feathers [authentication client](./client.md)) uses the returned access token to authenticate
81130

82-
2. With an existing access token, e.g. obtained through the Facebook mobile SDK
83-
- Authenticate normally through the [authentication service](./service.md) with `{ strategy: '<name>', accessToken: 'oauth access token' }`.
84-
- Calls the [OauthStrategy](#oauthstrategy) which
85-
- Gets the users profile
86-
- Finds or creates the entity for that profile
131+
This flow does **not** require the provider name in [`authStrategies`](./service.md#configuration). See [Configuration and security](#configuration-and-security).
132+
133+
2. With an existing provider access token (for example from a mobile SDK)
134+
135+
- Authenticate through the [authentication service](./service.md) with a request like `{ strategy: '<name>', accessToken: '<provider access token>' }` (some providers use `access_token` or an ID token instead).
136+
- The strategy must obtain the user profile by **verifying that token with the provider** (userinfo endpoint, Graph API, ID token verification, and so on).
137+
- Finds or creates the entity for that profile
87138
- Returns the authentication result
88139

89140
<BlockQuote type="warning" label="Important">
90141

91-
If you are attempting to authenticate using an existing oAuth access token, ensure that you have added the strategy (e.g. 'facebook') to the allowed [authStrategies](./service.md#configuration) configuration.
142+
Flow #2 needs **both** of the following:
143+
144+
1. The strategy name (for example `'facebook'`) in the allowed [`authStrategies`](./service.md#configuration) configuration so clients can call `POST /authentication`.
145+
2. An overridden [`getProfile`](#getprofile-data-params) that derives identity only from a **verified** provider response. Do not trust a client-supplied `profile` or `sub`.
146+
147+
The default `OAuthStrategy` does not call the provider when given only a client `profile`. Without a verifying `getProfile`, listing the provider in `authStrategies` is a serious security misconfiguration. See the [Facebook](../../cookbook/authentication/facebook.md) cookbook (Graph API with the access token) and the [Firebase](../../cookbook/authentication/firebase.md) cookbook (`verifyIdToken`) for correct patterns.
92148

93149
</BlockQuote>
94150

@@ -254,9 +310,15 @@ Here is a [list of all Grant configuration options](https://github.com/simov/gra
254310

255311
`oauthStrategy.getEntityData(profile, existing, params) -> Promise` returns the data to either create a new or update an existing entity. `entity` is either the existing entity or `null` when creating a new entity.
256312

313+
By default this only sets the provider id field (for example `googleId`). Custom strategies often also copy `email` or avatar fields from the profile. Prefer setting sensitive identity fields such as email when **creating** a user, or through an authenticated account-linking step. Avoid blindly rewriting recovery email on every login from profile data.
314+
257315
### getProfile(data, params)
258316

259-
`oauthStrategy.getProfile(data, params) -> Promise` returns the user profile information from the OAuth provider that was used for the login. `data` is the OAuth callback information which normally contains e.g. the OAuth access token.
317+
`oauthStrategy.getProfile(data, params) -> Promise` returns the user profile used for the login.
318+
319+
**Default behavior:** returns `data.profile` from the authentication payload. In the browser redirect flow that profile is set **server-side** by the OAuth callback after Grant completes. The default method does **not** call the provider itself.
320+
321+
When authenticating with a client-held provider token ([flow #2](#flow)), override `getProfile` to verify the token with the provider and build the profile from that verified response. Never treat a client-supplied `profile` as verified identity.
260322

261323
### getRedirect (data)
262324

@@ -305,7 +367,10 @@ declare module './declarations' {
305367
class MyGithubStrategy extends OAuthStrategy {
306368
async getEntityData(profile: OAuthProfile) {
307369
// Include the `email` from the GitHub profile when creating
308-
// or updating a user that logged in with GitHub
370+
// or updating a user that logged in with GitHub.
371+
// Profile data is only trustworthy after a real provider flow
372+
// (redirect callback or verified token login). Prefer create-time
373+
// or authenticated linking if email is a recovery channel.
309374
const baseData = await super.getEntityData(profile)
310375

311376
return {

docs/api/authentication/service.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ The following options are available:
6363

6464
- `secret`: The JWT signing secret.
6565
- `service`: The path of the entity service
66-
- `authStrategies`: A list of authentication strategy names to allow on this authentication service to create access tokens.
66+
- `authStrategies`: A list of authentication strategy names allowed for **external** `create` calls (`POST /authentication` / `app.service('authentication').create`). Typical values are `jwt`, `local`, API keys, and only those custom strategies that accept external credentials. [OAuth](./oauth.md) providers used solely via the `/oauth/<provider>` redirect flow should be [registered](#register-name-strategy) and configured under `authentication.oauth`, but are usually **omitted** from this list. See [OAuth configuration and security](./oauth.md#configuration-and-security).
6767
- `parseStrategies`: A list of authentication strategies that should be used to parse HTTP requests. Defaults to the same as `authStrategies`.
6868
- `entity`: The name of the field that will contain the entity after successful authentication. Will also be used to set `params[entity]` (usually `params.user`) when using the [authenticate hook](./hook). Can be `null` if no entity is used (see [stateless tokens](../../cookbook/authentication/stateless.md)).
6969
- `entityId`: The id property of an entity object. Only necessary if the entity service does not have an `id` property (e.g. when using a custom entity service).
@@ -95,7 +95,7 @@ An authentication service configuration in `config/default.json` can look like t
9595

9696
</BlockQuote>
9797

98-
Additionally to the above configuration, most [strategies](./strategy.md) will look for their own configuration under the name it was registered. An example can be found in the [local strategy configuration](./local.md#configuration).
98+
Additionally to the above configuration, most [strategies](./strategy.md) will look for their own configuration under the name it was registered. An example can be found in the [local strategy configuration](./local.md#configuration). OAuth provider settings live under `authentication.oauth` (see [OAuth options](./oauth.md#options)), which is separate from `authStrategies`.
9999

100100
## Authentication flows
101101

docs/cookbook/authentication/facebook.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ The client id (App ID) and secret can be found in the Settings of the [Facebook
4444

4545
The standard OAuth strategy only returns the default profile fields (`id` and `name`). To get other fields, like the email or profile picture, the [getProfile](../../api/authentication/oauth.md#getprofile-data-params) method of the [OAuth strategy needs to be customized](../../api/authentication/oauth.md#customization) to call the Graph API profile endpoint `https://graph.facebook.com/me` with an HTTP request library like [Axios](https://developers.facebook.com/tools/explorer/) requesting the additional fields.
4646

47+
This `getProfile` pattern (call Graph with the provider access token) is also **required** if you put `"facebook"` in [`authStrategies`](../../api/authentication/service.md#configuration) so clients can authenticate with `{ strategy: 'facebook', accessToken: '...' }` on `POST /authentication`. Never accept a client-supplied `profile` as identity. Browser-only Facebook login via `/oauth/facebook` does not need the provider in `authStrategies`. See [OAuth configuration and security](../../api/authentication/oauth.md#configuration-and-security).
48+
4749
> __Pro tip:__ Facebook API requests can be tested via the [Graph API explorer](https://developers.facebook.com/tools/explorer/).
4850
4951
The following example allows to log in with Facebook in the [chat application from the guide](../../guides/index.md):

docs/cookbook/authentication/firebase.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Update `config/default.json`:
1616
```json
1717
{
1818
"authentication": {
19+
"authStrategies": ["jwt", "firebase"],
1920
"oauth": {}
2021
},
2122
"firebase": {
@@ -28,6 +29,8 @@ Update `config/default.json`:
2829
```
2930
> Note: Since Firebase can be used for more than just authentication, we'll store our service account in the root of our config. Otherwise, if preferred, you can store under `authentication.oauth`.
3031
32+
`"firebase"` must be listed in `authStrategies` because clients authenticate with `POST /authentication` (flow #2). That is only safe because `getProfile` below calls `verifyIdToken` — never trust a client-supplied profile. See [OAuth configuration and security](../../api/authentication/oauth.md#configuration-and-security).
33+
3134
## Authentication Strategy
3235

3336
Create a file under `src/firebase.js`:

docs/cookbook/authentication/google.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,6 @@ module.exports = app => {
111111
```
112112
**Important**: googleId, profilePicture and email are properties that should exist on the database model!
113113

114+
Browser Google login uses `/oauth/google`. You do **not** need to add `"google"` to `authentication.authStrategies` for that redirect flow. Only add it if you implement verified provider-token login on `POST /authentication` — see [OAuth configuration and security](../../api/authentication/oauth.md#configuration-and-security).
115+
114116

docs/guides/basics/login.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ export const authentication = (app: Application) => {
239239

240240
<BlockQuote type="info">
241241

242-
For more information about the OAuth flow and strategy see the [OAuth API documentation](../../api/authentication/oauth.md).
242+
For more information about the OAuth flow and strategy see the [OAuth API documentation](../../api/authentication/oauth.md). Generated apps keep OAuth providers under `authentication.oauth` and **out** of public `authStrategies` on purpose so browser login uses `/oauth/github` only. See [OAuth configuration and security](../../api/authentication/oauth.md#configuration-and-security).
243243

244244
</BlockQuote>
245245

docs/guides/cli/authentication.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,6 @@ export const authentication = (app: Application) => {
3030

3131
## oAuth
3232

33-
Note that when selecting oAuth logins (Google, Facebook, GitHub etc.), the standard registered oAuth strategy only uses the `<name>Id` property to create a new user. This will fail validation against the default user [schema](./service.schemas.md) which requires an `email` property to exist. If the provider (and user) allows fetching the email, you can customize the oAuth strategy like shown for GitHub in the [oAuth authentication guide](../basics/authentication.md#login-with-github). You can also make the email in the schema optional with `email: Type.Optional(Type.String())`.
33+
When you select oAuth logins (Google, Facebook, GitHub etc.), the generator registers each provider strategy and adds it under `authentication.oauth` in configuration. Provider names are **not** added to `authentication.authStrategies` — browser SSO uses `/oauth/<provider>` instead of `POST /authentication`. See [OAuth configuration and security](../../api/authentication/oauth.md#configuration-and-security).
34+
35+
Note that the standard registered oAuth strategy only uses the `<name>Id` property to create a new user. This will fail validation against the default user [schema](./service.schemas.md) which requires an `email` property to exist. If the provider (and user) allows fetching the email, you can customize the oAuth strategy like shown for GitHub in the [oAuth authentication guide](../basics/authentication.md#login-with-github). You can also make the email in the schema optional with `email: Type.Optional(Type.String())`.

docs/guides/cli/default.json.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ These options are used directly in the generated application
5353

5454
### authentication
5555

56-
`authentication` contains the configuration for the authentication service and strategies. See the [authentication service configuration](../../api/authentication/service.md#configuration) for more information. For strategy specific settings refer to the [jwt](../../api/authentication/jwt.md#options), [local](../../api/authentication/local.md#options) and [oAuth](../../api/authentication/oauth.md#options) API documentation.
56+
`authentication` contains the configuration for the authentication service and strategies. See the [authentication service configuration](../../api/authentication/service.md#configuration) for more information. For strategy specific settings refer to the [jwt](../../api/authentication/jwt.md#options), [local](../../api/authentication/local.md#options) and [oAuth](../../api/authentication/oauth.md#options) API documentation. `authStrategies` lists strategies allowed on external `POST /authentication` (usually `jwt` and `local`). OAuth provider keys live under `authentication.oauth` and are separate from that list — see [OAuth configuration and security](../../api/authentication/oauth.md#configuration-and-security).
5757

5858
### Databases
5959

docs/guides/security.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Here are some things that you should be aware of when writing your app to make s
2323
- Escape any SQL (typically done by the SQL library) to avoid SQL injection.
2424
- JSON Web Tokens (JWT's) are only signed. They are **not** encrypted. Therefore, the payload can be examined on the client. This is by design. **DO NOT** put anything that should be private in the JWT `payload` unless you encrypt it first.
2525
- Don't use a weak `secret` for your token service. The generator creates a strong one for you automatically. No need to change it.
26+
- **OAuth / SSO:** Prefer the browser [redirect flow](../api/authentication/oauth.md#flow) (`/oauth/<provider>`). Register OAuth strategies and configure them under `authentication.oauth`, but do **not** put provider names in [`authStrategies`](../api/authentication/service.md#configuration) unless you intentionally support direct provider-token login **and** verify those tokens in `getProfile`. Never treat a client-supplied provider `profile` or `sub` as proof of identity. Details: [OAuth configuration and security](../api/authentication/oauth.md#configuration-and-security).
2627

2728
## Technologies used
2829

0 commit comments

Comments
 (0)