Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Table of Contents

...

  1. Let third party (web) applications securely access a user’s data in Synapse.  Today such applications must either
    1. predownload/embed data,
    2. use the application author’s Synapse credentials, or
    3. prompt the user for their Synapse credentials
  2. Let a headless batch job (e.g.,a “workflow”) securely access a user’s data in Synapse.  Today such a process must either
    1. Use predownloaded data
    2. Use the job runner’s Synapse credentials

Brief Overview of OAuth2

...

This document presupposes a basic (not necessarily thorough) level of understanding of OAuth2. There are four authorization grant flows in the OAuth spec. They can be summarized:

  • Authorization Code grant (most secure, client secret confidentiality must be guaranteed)
    1. Upon user consent, an OAuth client (3rd party) is granted an authorization code
    2. The authorization code can be used with a client secret to obtain a scoped access token.
    3. The access token can be used to access resources until it expires or is revoked
    4. The access token can be refreshed by the client with a refresh token and the client secret.
  • Implicit code grant (client secret confidentiality cannot be guaranteed)
    1. Upon user consent, an OAuth client (3rd party) is granted a scoped access token.
    2. The token can be used to access resources until it is expired or revoked, but it cannot be refreshed. The time that the token is active is typically very short (minutes).
  • Resource owner password credentials (not secure, especially with an untrusted client)
    1. The user provides their username and password to the OAuth client
    2. The OAuth client uses the credentials to obtain a scoped access token
  • Client credentials (used for cases where clients manage their own resources, i.e. not really authorization delegation)
    1. The OAuth client can request an access token with their client ID and client secret

...

These endpoints are necessary for users to approve/reject OAuth2.0 access requests

VerbEndpointPurposeRequest Object/ParamsResponse Object/ParamsNotes
GET
POST/
oauth2
login/
detailsGet human-interpretable details about the requesting client, and the scope that they are requesting

Parameters

clientId: Unique (the ID of an existing OAuth2 client requesting access)
scopedGet a scoped access token

sessionToken: String

scope: String

OAuth2Client

scopes: Array<string> 

e.g. [("read", "syn123"), ("create","syn456")]

(actual representation TBD)

The web layer can use this to get details about a client requesting authorization and the scope they requestGET/oauth2/auth

Display the login/consent info to the user and prompt for an accept/reject

URL Parameters:

response_type: String (always "code")

client_id: Unique

redirect_uri: String (points to OAuth client)

scope: String

state: String

Web interface for Synapse authorization

The form should permit login and we must be able to include the request parameters in a new request

This endpoint should point to a web layer that can show a UI with a login form and display the access that the user can consent to, along with a prompt for the user to accept/reject.

We should think about using login cookies here to simplify the UX if a user is already logged into Synapse

POST/oauth2/consentThe user grants access to the OAuth2 Client to access protected resources

URL Parameters:

response_type: String (always "code")

client_id: Unique

redirect_uri: String (points to OAuth client)

scope: String

state: String

If login is successful:

Redirect URL:

redirect_uri (provided in request)

Parameters:

code: the authorization code

state: the same value in the request

Who should execute this? The User Agent or the Web Layer on behalf of the user agent?

Question: how to handle with various Synapse IdPs? (E.g. Synapse users who sign in with Google accounts).

The "state" parameter is designed to avoid CSRF attacks. More info.

POST/oauth2/revoke

A logged in user can revoke OAuth2 client access using this method.

OAuth2RevokeRequest

client_id: unique

Is there a need for more granularity?

None

Revoking access not in the OAuth2 spec but allowing users to revoke client access may be important.

Token Requests

These endpoints would be used by OAuth2.0 clients to retrieve tokens with an access code

VerbEndpointPurposeRequestResponseNotesPOST/oauth2/tokenCalled by a client to get an access token

Body:

OAuth2AuthorizationCodeTokenRequest

scopedLoginResponse:

scopedSessionToken: String

acceptsTermsOfUse: Boolean

scope: String

exp: Integer (seconds until expiry)

This is a more secure alternative to the current session token as limits what can be done with the session token.

These can (should?) expire quickly (minutes-hours).

GET/oauth2/detailsGet human-interpretable details about the requesting client, and the scope that they are requesting

Parameters

clientId: Unique (the ID of an existing OAuth2 client requesting access)

scope: String

OAuth2Client

scopes: Array<string> 

e.g. [("read", "syn123"), ("create","syn456")]

(actual representation TBD)

The web layer can use this to get details about a client requesting authorization and the scope they request
POST/oauth2/consentThe user grants access to the OAuth2 Client to access protected resources

URL Parameters:

response_type: String (always "code")

client_id: Unique

redirect_uri: String (points to OAuth client)

scope: String

state: String

If login is successful:

Redirect URL:

redirect_uri (provided in request)

Parameters:

code: the authorization code

state: the same value in the request

Who should execute this? The User Agent or the Web Layer on behalf of the user agent?

Question: how to handle with various Synapse IdPs? (E.g. Synapse users who sign in with Google accounts).

The "state" parameter is designed to avoid CSRF attacks. More info.

POST/oauth2/revoke

A logged in user can revoke OAuth2 client access using this method.

OAuth2RevokeRequest

client_id: unique

Is there a need for more granularity?

None

Revoking access not in the OAuth2 spec but allowing users to revoke client access may be important.

Token Requests

These endpoints would be used by OAuth2.0 clients to retrieve tokens with an access code

VerbEndpointPurposeRequestResponseNotes
POST/oauth2/tokenCalled by a client to get an access token

Body:

OAuth2AuthorizationCodeTokenRequest

grant_type: String (always "authorization_code" for this call)

code: String (the authorization code)

redirect_uri: String (should be the same as previous redirect uri)

client_id: Unique

client_secret: String

Body:

OAuth2AccessToken

access_token: String

token_type: String ("Bearer")

expires_in: Integer (seconds)

refresh_token: String

(optionally, scope)

The redirect URI should be validated here before granting a token, along with the credentials in the request. More info.

The token type in almost all OAuth2 cases is "Bearer". We can use a different token type (e.g. HMAC, or make our own) if we want to, but there is probably no need. More info.

POST/oauth2/token/refreshCalled by a client to refresh an access token

Body:

OAuth2AuthorizationCodeRefreshTokenRequest

grant_type: String (always "

authorization

refresh_

code" for this call)

code: String (the authorization code)

redirect_uri: String (should be the same as previous redirect uri)

token" for this call)

refresh_token: String

client_id: Unique

client_secret: String

Body:

OAuth2AccessToken

access_

token: String

token

_type

: String

("Bearer")

expires_in: Integer (seconds)

refresh_token: String

(optionally, scope)

The redirect URI should be validated here before granting a token, along with the credentials in the request. More info.

The

token

type in almost all OAuth2 cases is "Bearer". We can use a different token type (e.g. HMAC, or make our own) if we want to, but there is probably no need. More info.POST

_type: String ("Bearer")

expires_in: Integer (seconds)

refresh_token: String


GET

/oauth2/token/introspect

or

/oauth2/token/

refreshCalled by a client to refresh an access tokenrefresh_token

info

Clients can determine if an authentication token is valid (and get scope, if it is opaque in the token)

Body:

OAuth2AuthorizationCodeRefreshTokenRequest

grant_type: String (always "refresh_token" for this call)

OAuth2TokenIntrospectionRequest

token: String

client_id: Unique

client_secret: String

Body:

OAuth2AccessToken

access_token: String

token_type: String ("Bearer")

expires_in: Integer (seconds)

refresh_token: String

GET

/oauth2/token/introspect

or

/oauth2/token/info

Clients can determine if an authentication token is valid (and get scope, if it is opaque in the token)

Body:

OAuth2TokenIntrospectionRequest

token: String

client_id: Unique

client_secret: String

Body:

OAuth2TokenIntrospectionResponse

active: Boolean

client_id: Unique

username: String (principal of user who authorized)

exp: Date (seconds until expiration)

scope: String

This endpoint is not strictly necessary, but we should strongly consider including this if we decide to not include scope with the access token

Web Layer

OAuth2TokenIntrospectionResponse

active: Boolean

client_id: Unique

username: String (principal of user who authorized)

exp: Date (seconds until expiration)

scope: Array<String> (human-interpretable scope)

This endpoint is not strictly necessary, but we should strongly consider including this if we decide to not include scope with the access token

Web Layer Interfaces (Portal)

The portal needs to implement interfaces for handling the components of OAuth2 that are best accomplished through user interfaces.

PagePurposeActions
OAuth2 AuthenticationProvides an interface for the user to authenticate in order to manage OAuth2 requests

Prompt for authentication (u:p/OAuth login)

Retrieve a scoped access token on behalf of the user (which can only be used to authorize OAuth2 authorization requests)

Redirect to/render OAuth2.0 Authorization Request

OAuth2 AuthorizationProvides an interface for the user to approve/reject OAuth requests

Retrieve/interpret client details + scope and display to the user "Do you want Client123 to have full access over syn123"

Approve/reject OAuth2 request on behalf of the user

Redirect user-agent to OAuth2AuthorizedUrl (provided by backend)

Diagrams to show where these API endpoints would be used and what objects/params are needed:

Image RemovedImage Added


How do we know if we're up-to-spec when we are done?

...

If we do not implement OIDC (just OAuth2) there doesn't seem to be any certification process that I can find. We may have to read the spec ourselves and hope we don't miss anything with thorough tests, future security audits, etc.

What is "scope"?

JIRAs(?): 

Jira Legacy
serverSystem JIRA
columnskey,summary,type,created,updated,due,assignee,reporter,priority,status,resolution
serverIdba6fb084-9827-3160-8067-8ac7470f78b2
keyPLFM-5170

...

The administrative port should not be exposed to public internet traffic. If you want to expose certain endpoints, such as the /clients endpoint for OpenID Connect Dynamic Client Registry, you can do so but you need to properly secure these endpoints with an API Gateway or Authorization Proxy. 

Do we need this?

Spring Security

...