Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 74 additions & 9 deletions docs/api/authentication/oauth.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,55 @@ The following settings for `app.configure(oauth())` are available:
- `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.**
- `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.**

### Configuration and security

OAuth setup uses three separate pieces. Only the first two are required for the usual browser redirect login:

| Piece | Role |
| --- | --- |
| `authentication.register('google', new OAuthStrategy())` | Registers the strategy so the OAuth callback can run it |
| `authentication.oauth.google` in configuration | Grant provider options (`key`, `secret`, `scope`, …) |
| `authentication.authStrategies` | Strategy names clients may use on **external** `POST /authentication` |

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).

The [Feathers generator](../../guides/cli/authentication.md) already follows this pattern: OAuth providers are configured under `authentication.oauth`, while `authStrategies` typically stays `["jwt", "local"]`.

```json
// Typical safe config for browser-only OAuth (matches the generator)
{
"authentication": {
"authStrategies": ["jwt", "local"],
"oauth": {
"google": {
"key": "<Client ID>",
"secret": "<Client secret>"
}
}
}
}
```

```json
// Unsafe for the default OAuthStrategy when you only need browser SSO.
// Do not list provider names here unless you implement verified token login (flow #2).
{
"authentication": {
"authStrategies": ["jwt", "local", "google", "microsoft"]
}
}
```

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

Putting an OAuth provider name (for example `google` or `github`) in [`authStrategies`](./service.md#configuration) exposes that strategy on external `POST /authentication`.

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.

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.

</BlockQuote>

### Providers

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

2. With an existing access token, e.g. obtained through the Facebook mobile SDK
- Authenticate normally through the [authentication service](./service.md) with `{ strategy: '<name>', accessToken: 'oauth access token' }`.
- Calls the [OauthStrategy](#oauthstrategy) which
- Gets the users profile
- Finds or creates the entity for that profile
This flow does **not** require the provider name in [`authStrategies`](./service.md#configuration). See [Configuration and security](#configuration-and-security).

2. With an existing provider access token (for example from a mobile SDK)

- 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).
- The strategy must obtain the user profile by **verifying that token with the provider** (userinfo endpoint, Graph API, ID token verification, and so on).
- Finds or creates the entity for that profile
- Returns the authentication result

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

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.
Flow #2 needs **both** of the following:

1. The strategy name (for example `'facebook'`) in the allowed [`authStrategies`](./service.md#configuration) configuration so clients can call `POST /authentication`.
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`.

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.

</BlockQuote>

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

`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.

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.

### getProfile(data, params)

`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.
`oauthStrategy.getProfile(data, params) -> Promise` returns the user profile used for the login.

**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.

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.

### getRedirect (data)

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

return {
Expand Down
4 changes: 2 additions & 2 deletions docs/api/authentication/service.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ The following options are available:

- `secret`: The JWT signing secret.
- `service`: The path of the entity service
- `authStrategies`: A list of authentication strategy names to allow on this authentication service to create access tokens.
- `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).
- `parseStrategies`: A list of authentication strategies that should be used to parse HTTP requests. Defaults to the same as `authStrategies`.
- `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)).
- `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).
Expand Down Expand Up @@ -95,7 +95,7 @@ An authentication service configuration in `config/default.json` can look like t

</BlockQuote>

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).
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`.

## Authentication flows

Expand Down
2 changes: 2 additions & 0 deletions docs/cookbook/authentication/facebook.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ The client id (App ID) and secret can be found in the Settings of the [Facebook

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.

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).

> __Pro tip:__ Facebook API requests can be tested via the [Graph API explorer](https://developers.facebook.com/tools/explorer/).

The following example allows to log in with Facebook in the [chat application from the guide](../../guides/index.md):
Expand Down
3 changes: 3 additions & 0 deletions docs/cookbook/authentication/firebase.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Update `config/default.json`:
```json
{
"authentication": {
"authStrategies": ["jwt", "firebase"],
"oauth": {}
},
"firebase": {
Expand All @@ -28,6 +29,8 @@ Update `config/default.json`:
```
> 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`.

`"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).

## Authentication Strategy

Create a file under `src/firebase.js`:
Expand Down
2 changes: 2 additions & 0 deletions docs/cookbook/authentication/google.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,6 @@ module.exports = app => {
```
**Important**: googleId, profilePicture and email are properties that should exist on the database model!

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).


2 changes: 1 addition & 1 deletion docs/guides/basics/login.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ export const authentication = (app: Application) => {

<BlockQuote type="info">

For more information about the OAuth flow and strategy see the [OAuth API documentation](../../api/authentication/oauth.md).
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).

</BlockQuote>

Expand Down
4 changes: 3 additions & 1 deletion docs/guides/cli/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,6 @@ export const authentication = (app: Application) => {

## oAuth

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())`.
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).

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())`.
2 changes: 1 addition & 1 deletion docs/guides/cli/default.json.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ These options are used directly in the generated application

### authentication

`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.
`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).

### Databases

Expand Down
1 change: 1 addition & 0 deletions docs/guides/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Here are some things that you should be aware of when writing your app to make s
- Escape any SQL (typically done by the SQL library) to avoid SQL injection.
- 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.
- Don't use a weak `secret` for your token service. The generator creates a strong one for you automatically. No need to change it.
- **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).

## Technologies used

Expand Down
Loading