Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auth UI #411

Closed
54 changes: 54 additions & 0 deletions features/202-session-management/breaking-changes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Delete RefreshTokenDisabled

## Old SDK and old gateway behavior

When the SDK receives a response with `x-skygear-try-refresh-token: true`, it tries to refresh the access token.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can try using silent authentication.

  1. Login at authorize, set cookie at AuthUI
  2. On app launch/expired -> call authorize with prompt = none
  3. AuthUI check cookie session: yes -> return code, no -> return 401
  4. Receive code & send token request, receive access_token + expires_in, NO REFRESH TOKEN
  5. When expires (according to expires_in): go to 1
  6. When receive 401: either revoked, or custom 401 -> goto 1


The gateway uses this flag to determine whether it should terminate the request with 401.
If RefreshTokenDisabled is true, then the request is not terminated and continue to the upstream server.
If RefreshTokenDisabled is false, then the request is terminated with 401 and the header `x-skygear-try-refresh-token: true` is added.

## Old SDK and new gateway behavior

When the SDK receives a response with `x-skygear-try-refresh-token: true`, it tries to refresh the access token.

The gateway never terminate request and write `x-skygear-session-valid` instead. It is up to the upstream server to return 401.
The gateway writes `x-skygear-try-refresh-token: true` if `x-skygear-session-valid: false`.

The refresh access token flow is _NOT_ broken.

## New SDK and new gateway behavior

When the SDK receives a response with `x-skygear-try-refresh-token: true` and it is configured to use `Authorization:`, it tries to refresh the access token.

The gateway never terminate request and write `x-skygear-session-valid` instead. It is up to the upstream server to return 401.
The gateway writes `x-skygear-try-refresh-token: true` if `x-skygear-session-valid: false`.

The refresh access token flow is only triggered when the SDK is using `Authorization:`.

# Delete SessionTransport

## Old SDK and old server behavior

The SDK expects the server to include the access token either in cookie or the response body. If the access token is returned in body, subsequent requests have `Authorization:` set.

The server either writes the access token in cookie or includes it in the response body, according to the setting of SessionTransport.

## Old SDK and new server behavior

The SDK expects the server to include the access token either in cookie or the response body. If the access token is returned in body, subsequent requests have `Authorization:` set.

Skygear Gateway has special routing rules that proxy `https://{app_domain}/_{gear}/` to `https://{gear_domain}/`.
Skygear Gateway add a special header `x-skygear-legacy-sdk: true` to include such case.
Auth Gear in this case always include the access token in cookie and in the response body.

The request made by the SDK has session specified in cookie and `Authorization:`.
Cookie has higher precedence.
In case the session is invalid, the cookie is cleared by the gateway. The SDK triggers the refresh access token flow but it never succeed.

## New SDK and new server behavior

The SDK uses `Authorization:` for requests to gears and uses the configured transport for requests to the app domain.

Auth Gear in this case always include the access token in the response body.
A special endpoint is mounted in the app domain to accept the access token and the refresh token, and write the session token in cookie.
76 changes: 39 additions & 37 deletions features/202-session-management/config.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,59 @@
# Session Configuration
# Client configuration

## Sample configuration

In user configuration:
```yaml
clients:
- name: Web App
disabled: false
api_key: XXX
session_transport: cookie
access_token_lifetime: 1800
session_idle_timeout_enabled: true
session_idle_timeout: 300
same_site: lax
- name: iOS App
disabled: false
api_key: YYY
session_transport: header
access_token_lifetime: 1800
refresh_token_disabled: false
refresh_token_lifetime: 86400
session_idle_timeout_enabled: false
session_idle_timeout: 300
- redirect_uris:
- "https://example.com"
client_name: Web App
logo_uri: "https://example.com/logo.png"
disabled: false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this, spec is not updated.

api_key: XXX
access_token_lifetime: 1800
session_idle_timeout_enabled: true
session_idle_timeout: 300
same_site: lax
- redirect_uris:
- "myapp://host/path"
client_name: iOS App
logo_uri: "https://example.com/logo.png"
disabled: false
api_key: YYY
access_token_lifetime: 1800
session_lifetime: 86400
session_idle_timeout_enabled: false
session_idle_timeout: 300
```

## User Configuration
## Parameters

### Client Metadata

Some parameters are defined in OIDC. See [ClientMetadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata)

They are

- `redirect_uris`
- `client_name`
- `logo_uri`

### Skygear-specific

The value of key `clients` is a list of client configuration:
- `name`: Name of client. Show in UI (e.g. portal)
- `disabled`: Indicate whether the client is disabled.
- `api_key`: API key.
- `session_transport`: The transport method of session tokens.
Can be `cookie` or `header`.
- `access_token_lifetime`: The lifetime of access token in seconds, default
to 1800.
- `refresh_token_disabled`: (valid for `header` transport only)
Indicate whether refresh token is disabled, default
to `false`. If `session_transport` is `cookie`,
refresh token is disabled and this configuration
has no effect.
- `refresh_token_lifetime`: (valid for `header` transport only)
The maximum lifetime of refresh token in seconds,
- `api_key`: API key. It is going to be used as `client_id`.
- `access_token_lifetime`: The lifetime of access token in seconds, default to 1800.
- `session_lifetime`: The maximum lifetime of session in seconds,
default to max(`access_token_lifetime`, 86400).
Must greater than or equal to `access_token_lifetime`.
- `session_idle_timeout_enabled`: Indicate whether session idle timeout is
enabled, default to `false`.
- `session_idle_timeout`: The session idle timeout in seconds,
default to min(`access_token_lifetime`, 300).
Must less than or equal to `access_token_lifetime`.
- `same_site`: (valid for `cookie` transport only)
The `SameSite` property of cookie. Can be `lax`, `strict`, or
- `same_site`: The `SameSite` property of cookie. Can be `lax`, `strict`, or
`none`. Default to `lax`.

## Auth Gear standalone configuration
## Auth Gear environment variable
- `INSECURE_COOKIE`: Indicate whether session cookie should not set the `Secure`
flag. Default to `false`.
80 changes: 42 additions & 38 deletions features/202-session-management/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ Sessions of auth gear should be configurable and manageable.

- App Developer/User should be able to manage sessions.
- App Developer should be able to configure session lifetime.
- App Developer should be able to configure session token transport.


# Proposed Design
Expand Down Expand Up @@ -55,34 +54,50 @@ to be revoked.
Update session API allows updating name of session. Custom attributes can also
be updated using master key.

## Different kinds of session

## Access / Refresh Token
### OIDC session

Each session would have an access token, and optionally a refresh token. Both
type of tokens are opaque: token formats are unspecified and developer should
not attempt to interpret the content of tokens.
The sessions created by JSON API or OIDC flow have `client_id`.

Access token would always have a lifetime; access token would be treated as
invalid after its expiry.
### OP session

The sessions created by the web UI of Auth Gear does not have `client_id` because Auth Gear is acting as an OP.

### Revoking sessions

The user can revoke session of any kinds. However, revoking a OIDC session does not necessarily cause logout in the RP.

When the RP is a third-party app that has its session management.
When it receives a ID token from Auth Gear it creates its own session.
Revoking the OIDC session has no direct effect on the third-party app session, unless both Auth Gear and the third-party app supports OIDC Session Management which offers single logout.

If the RP is the same Skygear App, then revoking the OIDC session works seamlessly. It is because Skygear App does not maintain a separate session.

## Access token, refresh token and session token

Each session at least has an access token and a refresh token, and optionally a session token.
The format of all 3 tokens are opaque. The developer should not attempt to interpret the content of tokens.

Access token would always have a lifetime; access token would be treated as invalid after its expiry. The access token must be used in bearer token in `Authorization:` header.

Refresh token can be used to obtain a new access token. If the old access token
is still valid, the old access token is invalidated; there would be at most one
valid access token for a session at any time.
valid access token for a session at any time. The lifetime of the refresh token is the lifetime of the session.

Refresh token would always have a maximum lifetime.
Session token must be used in cookie. It identifies the session. The lifetime of the session token is the lifetime of the session.

Optionally, a session idle timeout can be specified: refresh token (or access
token if refresh token is disabled) must be used at least once before the
timeout, otherwise the session would be expired.
Optionally, a session idle timeout can be specified: either one token must be used at least once before the timeout, otherwise the session would be expired.

A session is invalidated if its identity is deleted, or its user is disabled.
A session is expired if its refresh token is expired, or its access token is
expired (if refresh token is disabled).
If a session is invalidated, expired, revoked, or logout, its associated access
token / refresh token would be treated as invalid.
A session is expired if it reaches its maximum lifetime.
If a session is invalidated, expired, revoked, or logout, its associated tokens
would be treated as invalid.

A session is invalid if it is invalidated, expired, revoked or logged out.

### Legacy refresh token flow

For auth gear, if the endpoint requires authentication and the session is detected as invalid,
the HTTP response includes a header `x-skygear-try-refresh-token: true`.

Expand All @@ -102,32 +117,25 @@ if refresh token is available; SDKs would not expose concept of access token /
refresh token; however, developers should still handle situation where session
is no longer valid due to various reasons.

### New refresh token flow

## Session Cookie
The flow is the same but the condition to happen is changed. The SDK must be configured to use `Authorization:` in order to trigger the flow. It is because the session token cannot be refreshed.

Optionally, developer can choose to use cookies to transport session tokens, in
order to facilitate server-side rendering.
## Session token

If developer choose to enable session cookie:
- Refresh token would not be issued. (See [Appendix](#session-cookie-and-refresh-token))
- Access token would be transported in session cookie.
- Access token would not be returned from APIs.
- Gateway would read access token from cookie only, ignoring `Authorization`
header. If a token is present in both header and cookie, it would be treated
as if session is not found.
- To prevent unrecoverable failure (i.e. site unaccessible until cookies are cleared),
the session cookie would be cleared when the session is not found or invalid,
instead of producing error response.
- Some SDKs (e.g. mobile SDKs) would not be able to consume it.
The session has a session token that must be used in cookie. Session token is essential for
server-side rendering app.

To obtain a session token for the app domain, the SDK calls a special endpoint in the app domain. The endpoint accepts the access token and the refresh token and return the session token in cookie.

### Security Consideration

- HTTPS is required (can be disabled for development purpose).
- Session cookie is HTTP-only; web app and web SDK would not have access to the
- Session token is HTTP-only; web app and web SDK would not have access to the
session tokens.
- Session cookie is set with `SameSite=lax` by default; generally, cross-domain
- Session token is set with `SameSite=lax` by default; generally, cross-domain
requests would not contain the cookie.
- Session cookie would not be shared across custom domains.
- Session token is host-only.
- App developers are responsible to prevent CSRF in their services.
- For untrusted multi-tenant hosting where sub-domain is allocated to each
tenant, the top-level domain must be included in Public Suffix List.
Expand Down Expand Up @@ -156,13 +164,8 @@ Developer can create a API key for each client. Each API key identifies a
specific client, and different attributes can be configured for the client:
- Name
- Enabled status
- Session token transport (cookie / `Authorization` header)
- Session token properties (e.g. lifetime)

Session token transport must be specified at client creation. Developer should
not change it after creation.


# Future Works
- Audit log should include session attributes for historical records (#340)
- A country value should be provided, deriving from available information (#354)
Expand All @@ -176,6 +179,7 @@ not change it after creation.

[Use cases](./use-cases.md)

[Breaking Changes](./breaking-changes.md)

## Session Cookie and Refresh Token

Expand Down
15 changes: 0 additions & 15 deletions features/256-auth-gear-interaction-overview-in-next/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,18 +285,3 @@ For admin user, it should support:
- ...

To support above functionalities, it would be provided by auth gear internally. It can connect auth gear DB directly, and no need to provide query interface to the public.

## [TBD] Auth UIKit

![](https://i.imgur.com/c4Vqk6G.png)

Consider skygear has a general purpose UIKit for user login/signup, the UI should response by following settings:

- auth criteria: username, email, phone number, ..., etc.
- auth protocols: SSO, LDAP, SAML, ..., etc.
- multi-factor authentication
- ...

![](https://i.imgur.com/0Rg7IOA.png)

It should be a separate service to handle such settings, and then Auth UIKit knows how to update its UI accordingly.
23 changes: 9 additions & 14 deletions features/268-next-cf/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,20 +311,15 @@ hooks:

# Route matching

To easily distinguish gear route and non-gear route, I suggest we add the following two rules:
- All gear routes start with `_`, e.g. auth gear routes are `/_auth/`, signup path would be `/_auth/signup`
- So any request with its path start with `_` would be considered request to gear
- Non-gear path cannot be start with `_`

Here is the route matching rules when a request enter the cluster:
- If the path starts with `_`, it would try find the gear by matching `/_{gear_name}` first
- If a gear is found, the request would be forwarded to that gear
- Otherwise, return function not found error
- Trailing slash in `path` is insignificant. That is, `/a` and `/a/` is equivalent.
- All paths are matched in a longest prefix match fashion.
- There is no partial match. That is, the pattern `/a` does not match the path `/apple`.
- The matched prefix of `http-service` is removed.
- If none of the registered path is matched, return not found error.
- Each gear has its own domain.
- The app has its own domain.
- If the request targets a gear, the request is proxied without route matching.
- The following route matching applies only to the app.
- Trailing slash in `path` is insignificant. That is, `/a` and `/a/` is equivalent.
- All paths are matched in a longest prefix match fashion.
- There is no partial match. That is, the pattern `/a` does not match the path `/apple`.
- The matched prefix of `http-service` is removed.
- If none of the registered path is matched, return not found error.

For example, with the following skycli configuration:

Expand Down
6 changes: 0 additions & 6 deletions features/270-next-skycli/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,9 +255,6 @@ app_config:
master_key: xxxxx
api_key: xxxxx
sso:
allowed_callback_urls:
- http://127.0.0.1:5000/
- http://localhost:5000/
merge_users_with_identical_email: true
```

Expand All @@ -280,9 +277,6 @@ app_config:
master_key: xxxxx
api_key: xxxxx
sso:
allowed_callback_urls:
- http://127.0.0.1:5000/
- http://localhost:5000/
merge_users_with_identical_email: true
~
~
Expand Down
Loading