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 |
---|---|
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 |
---|---|
200 | {"items":["healthCode1","healthCode2"], "requestParams": {...}, "type":"ForwardOnlyCursorPagedList"} |
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 |
---|---|
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 |
---|
Map<String,OauthProvider> oauthProviders (mapped to their vendor identifier |
Seems trivial enough to include in study.
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 |
---|
String studyId:vendor (hashKey) String healthCode (rangeKey) String accessToken String refreshToken Long createdOn Long expiresOn |
OAuthAccessToken |
---|
String vendorId String accessToken DateTime expiresOn |
OAuthAuthorizationToken |
---|
String vendorId |
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"
}