OAuth 2.0 Authorization Code Grant Workflow

OAuth 2.0 Authorization Code Grant Workflow

This design would allow us to support other OAuth "Authorization Code Grant" providers in the future.

API

authenticated users

POST /v3/oauth/:vendorId

authenticated users

POST /v3/oauth/:vendorId

body

{"authCode":"<authCode>"}

if auth code provided in post

get access and refresh tokens

no authCode provided, accessToken exists, not expired

return access token

no authCode provided, accessToken exists, expired

refresh access token, return new access token

401

no authCode, no accessToken, or an error

200

{"vendorId":"vendorId","accessToken":"<accessToken>","expiresOn":"<ISO 8601 timestamp>"}

 

Get method that returns health codes for accounts that have given Fitbit authorization (at some point). These might be some kind of minimal grant object as well (OAuthGrant[healthCode, accessToken, expiresOn]).

workers

GET /v3/studies/:studyIdentifier/oauth/:vendorId?pageSize=x&offsetKey=y

workers

GET /v3/studies/:studyIdentifier/oauth/:vendorId?pageSize=x&offsetKey=y

200

{"items":["healthCode1","healthCode2"], "requestParams": {...}, "type":"ForwardOnlyCursorPagedList"}

 

workers

GET /v3/studies/:studyIdentifier/oauth/:vendorId/:healthCode

workers

GET /v3/studies/:studyIdentifier/oauth/:vendorId/:healthCode

if access token exists and is not expired

return access token

if access token exists and is expired

refresh token and return refreshed token

401

anything else (should only be an error from Fitbit)

200

{"vendorId":"vendorId","accessToken":"<accessToken>","expiresOn":"<ISO 8601 timestamp>"}

OAuthService

Method

Description

Method

Description

OAuthAccessToken requestAccessToken(OAuthAuthorizationToken authToken)

retrieves the access token, making the necessary requests to the OAuth provider to refresh or whatever

ForwardCursorPagedResourceList<String> getHealthCodesGrantingAccess(StudyIdentifier studyId, String vendorIdentifier, int pageSize, String offsetKey)

retrieve all the health codes for accounts that have granted access to the OAuth provider at some point. They should all have refresh tokens and access tokens.

OAuthAccessToken getAccessToken(StudyIdentifier studyId, String vendorIdentifier, String healthCode)

retrieves an access token for the individual health code, making the necessary requests to the OAuth provider to refresh or whatever.

There's going to be some other classes that aren't that interesting:

  • OAuthProvider

  • OAuthAccessGrant

  • DynamOAuthAccessGrant

  • OAuthAccessGrantDao

  • DynamoOAuthAccessGrantDao

These are pretty standard design-wise.

DynamoDB Tables

Study

Study

Map<String,OauthProvider> oauthProviders (mapped to their vendor identifier

Seems trivial enough to include in study.

OAuthProvider

OAuthProvider

String clientId
String secret
String endpoint
String redirectUrl (maybe)
String state (maybe)

RedirectUrl and state... Fitbit documentation says that if these are provided by the client in the authorization step they have to be provided in this step and they have to match exactly. Or maybe they only have to match exactly if they are provided. We'll sort it out.

OAuthAccessGrant

OAuthAccessGrant

String studyId:vendor (hashKey)
String healthCode (rangeKey)
String accessToken
String refreshToken
Long createdOn
Long expiresOn

OAuthAccessToken

OAuthAccessToken

String vendorId
String accessToken
DateTime expiresOn

OAuthAuthorizationToken

OAuthAuthorizationToken

String vendorId
String authToken

UserSessionInfo

UserSessionInfo

Map<String,OAuthAccessToken> accessTokens

Fitbit access token response


This is the JSON returned by Fitbit... not sure if this is defined by the OAuth specification or not, but seems like it would have to be and can be standardized in code for the Authorization Code Grant workflow.

{
    "access_token": "eyJhbGciOTnSWz_qlqoEpUlpc",
    "expires_in": 3600,
    "refresh_token": "c643a63c072f0f05478e9d18b991db80ef6061e4f8e6c822d83fed53e5fafdd7",
    "token_type": "Bearer",
    "user_id": "26FWFL"
}