Skip to end of banner
Go to start of banner

Tracking OIDC Access Tokens

Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 2 Next »

Introduction

Most Synapse API calls require that the caller be identified (authentication), in order to determine what the caller can access (authorization). When a caller does not provide any form of identification, Synapse will treat the caller as a special user called Anonymous. While there is a lot of metadata available to the Anonymous user, most of the actual data can only be accessed (downloaded) by a caller that has been identified.

Historically, Synapse has supported four mechanisms for API callers to identify themselves:

type

description

status

BASIC

The user provides their username/password in header:

Authorization: Basic joe:password123

Deprecated and no longer functional.

APIKEY

The request is signed using cryptographic algorithm with a shared api-key

Deprecated but still functional.

SESSIONTOKEN

Login v1 returns a session token that the caller is expected to provide with each request.

Deprecated but still functional. When an old client requests an session_token they are actually issued a new access_token. Therefore, this route is functionally identical to the newer BEARERTOKEN.

BEARERTOKEN

For this case the caller is expected to provide a Synapse access token

Authorization: Bearer <access_token>

Actively documented and supported.

For this document, we will focus only on the BEARERTOKEN method of authentication/authorization.

Authorization: Bearer

Synapse follows the RFC 6750: The OAuth 2.0 Authorization Framework: Bearer Token Usage specification. Specifically, Synapse expects the following header to be include with each HTTPS request:

Authorization: Bearer <access_token>

For more information about how clients can acquire an <access_token> see: REST Docs: Authentication Services.

Synapse expects that the provided <access_token>, is a Synapse issued JSON Web Token (JWT). Synapse currently supports two types of JWT access_tokens for two different use cases:

  • Personal Access Token - Permanent / Long Term token used by long running processes and services.

  • OpenID Connect (OIDC) Access Token - Temporary / Short Term tokens used for interactive client sessions.

In order for a user to acquire either type of access token, they must first authenticate their identity. Once authenticated, one of these two bearer access tokens will be issued (depending on the context). It is important to emphasis that both of these access token types are bearer tokens. The bearer presents the access token as proof of authentication, so no further authentication is required.

Stolen Access Tokens

Since access tokens are bearer tokens, if a 3rd party were to steal another user’s access token, the 3rd party could use it to impersonate the token’s owner. For example, an access token can be acquired by a malicious party using a phishing scheme. It is also possible that the owner of an access token might accidentally make it available to others. Consider the cases where a user puts an access token directly into a script to run a quick test. If they forget about the access token, and later create a pull request that includes the script, they will unwitting share the access token with the world. In fact, this is such a common problem, that GitHub offers a “push protection” feature to help organizations deal with it.

Anytime a user realizes their access token has been compromised, they will need a way to deactivate/revoke it. Since Personal Access Tokens (PAT) are permanent/long term, Synapse already has a set of APIs to both list and revoke them. However, since OIDC Access Tokens automatically expire after 24 hours we did not provide services to list or revoke them.

In a recent, penetration test, the pen tester successful acquired an OIDC Access Token from a Synapse user via a phishing scheme. With no means to revoke the stolen token, its owner was unable to prevent the stolen OIDC access token from being used. In addition, “logging out” did not revoke the OIDC Access Token. for more information see: PLFM-8350 - Getting issue details... STATUS .

OIDC Access Token API Changes

With the current OIDC Access Token implementation, we do not need to store any information about the tokens in the database. Instead, we can validate each token by checking its cryptographic signature. However, this optimization means we have no mechanism to revoke OIDC Access Tokens.

To address this issue we propose adding tracking information for each OIDC Access Token issued to our database. This will allow us to support APIs for both listing and revoking outstanding OIDC access tokens.

Response

URL

Request

Description

Authorization

OIDCAccessTokenResponse

GET /user/{userId}/OIDCAccessToken

OIDCAccessTokenRequest

Get a paginated list of outstanding OIDC AccessTokens issued to the identified user.

Provided access token must belong to the {uesrId} or to an Admin.

DELETE /user/{userId}/OIDCAccessToken/all

Revoke all outstanding OIDC access tokens associated with the {userId}.

Provided access token must belong to the {uesrId} or to an Admin.

DELETE /OIDCAccessToken

Revoke the OIDC access token used to make this call. This can be used to “log out”.

Not for Admin use.

DELETE /user/{userId}/OIDCAccessToken/{tokenId}

Revoke a specific OIDC access token identified by {tokenId} that belongs to {userID}.

Provided access token must belong to the {uesrId} or to an Admin.

OIDCAccessTokenRequest.json

{
	"description": "Request for a single page of outstanding OIDCAccessTokenInfo for the given userId",
	"properties": {
		"userId": {
			"type": "string",
			"description": "The ID of the user that owns the access tokens"
		},
		"nextPageToken": {
			"type": "string",
			"description": "Optional. Forward the 'nextPageToken' from a previous response to get the next page."
		}
	}
}

OIDCAccessTokenResponse.json

{
	"description": "A single page of OIDCAccessTokenInfo with a nextPageToken to get more results.",
	"properties": {
		"page": {
			"type": "array",
			"description": "A single page of access tokens",
			"items": {
				"$ref": "org.sagebionetworks.repo.OIDCAccessTokenInfo"
			}
		},
		"nextPageToken": {
			"type": "string",
			"description": "Token that can be used to get the next page.  Null if there are no more results."
		}
	}
}

OIDCAccessTokenInfo.json

{
	"description": "Information about an outstanding OIDC access token",
	"properties": {
		"tokenId": {
			"type": "string",
			"description": "The unique ID of the OIDC access token"
		},
		"expiresOn": {
			"type": "string",
			"format": "date-time",
			"description": "The data-time when this access token expires."
		},
		"userId": {
			"type": "string",
			"description": "The ID of the user that owns this access token"
		}
	}
}

Open Questions

  • Should we attempt to capture additional information such as IP address and client when issuing OIDC Access Tokens?

  • Should we restrict the use of OIDC Access Tokens to same IP address that it was issued?

  • When a OIDC Access Token is used, should we track additional information like IP address and client?

  • No labels