-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #121 from sharetribe/auth-with-idp
Auth with idp
- Loading branch information
Showing
13 changed files
with
556 additions
and
139 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,7 +10,7 @@ the user is already logged in or not. | |
Logs in the user and returns a Promise. | ||
|
||
The session information will be saved to the SDK instance when the | ||
Promise is resolved. Subsequest requests will be made as the logged in | ||
Promise is resolved. Subsequent requests will be made as the logged in | ||
user. | ||
|
||
## Logout | ||
|
@@ -30,12 +30,23 @@ store](./token-store.md#memory-store). | |
|
||
Returns a Promise with an Object as a value. The object may contain two fields: | ||
|
||
* `scopes`: an array containing the scopes associated with the currently stored token | ||
* `isAnonymous`: a boolean denoting if the currently stored token only allows public read access | ||
- `scopes`: an array containing the scopes associated with the currently stored token | ||
- `isAnonymous`: a boolean denoting if the currently stored token only allows public read access | ||
|
||
To determine if the user is logged in, check if `isAnonymous` equals | ||
`false`. | ||
|
||
## Login with IdP | ||
|
||
**`sdk.loginWithIdp({ idpId: string, idpClientId: string, idpToken: string }) : Promise`** | ||
|
||
Logs in the user with information from an identity provider (e.g. Facebook) and returns a Promise. | ||
User can be authenticated if the `idpToken` can be verified and an identity provider account resolved from the token is associated with a Flex account or if an email address resolved from the token matches a verified email of a Flex account. | ||
|
||
The session information will be saved to the SDK instance when the | ||
Promise is resolved. Subsequent requests will be made as the logged in | ||
user. | ||
|
||
**Example:** | ||
|
||
```js | ||
|
@@ -59,42 +70,52 @@ example, to know the name of the logged in user, you need to call | |
## Authentication example | ||
Here's a full example how to log user in and out and determine the | ||
current authentication status. | ||
**Example:** | ||
```js | ||
const isLoggedIn = authInfo => authInfo && authInfo.isAnonymous === false; | ||
const isLoggedIn = (authInfo) => authInfo && authInfo.isAnonymous === false; | ||
|
||
sdk.authInfo().then(authInfo => { | ||
console.log(`Logged in: ${isLoggedIn(authInfo)}`) | ||
sdk | ||
.authInfo() | ||
.then((authInfo) => { | ||
console.log(`Logged in: ${isLoggedIn(authInfo)}`); | ||
// prints: "Logged in: false" | ||
|
||
return sdk.login({ username: '[email protected]', password: 'test-secret' }); | ||
}).then(loginRes => { | ||
return sdk.login({ | ||
username: "[email protected]", | ||
password: "test-secret", | ||
}); | ||
}) | ||
.then((loginRes) => { | ||
console.log("Login successful!"); | ||
|
||
return sdk.authInfo(); | ||
}).then(authInfo => { | ||
}) | ||
.then((authInfo) => { | ||
console.log(`Logged in: ${isLoggedIn(authInfo)}`); | ||
// prints: "Logged in: true" | ||
|
||
return sdk.currentUser.show(); | ||
}).then(userRes => { | ||
}) | ||
.then((userRes) => { | ||
const profile = userRes.data.data.attributes.profile; | ||
console.log(`Current user: ${profile.firstName} ${profile.lastName}`); | ||
|
||
return sdk.logout(); | ||
}).then(logoutRes => { | ||
}) | ||
.then((logoutRes) => { | ||
console.log("Logged out!"); | ||
|
||
return sdk.authInfo(); | ||
}).then(authInfo => { | ||
console.log(`Logged in: ${isLoggedIn(authInfo)}`) | ||
}) | ||
.then((authInfo) => { | ||
console.log(`Logged in: ${isLoggedIn(authInfo)}`); | ||
// prints: "Logged in: false" | ||
}).catch(res => { | ||
}) | ||
.catch((res) => { | ||
// An error occurred | ||
console.log(`Request failed with status: ${res.status} ${res.statusText}`); | ||
}); | ||
|
@@ -117,17 +138,16 @@ SDK with a token store that holds an access token. | |
```js | ||
const sdk = sharetribeSdk.createInstance({ | ||
clientId: 'a client ID', | ||
clientSecret: 'a client secret', | ||
clientId: "a client ID", | ||
clientSecret: "a client secret", | ||
tokenStore: sharetribeSdk.tokenStore.memoryStore(), | ||
}) | ||
}); | ||
|
||
sdk.login({ username: '[email protected]', password: 'test-secret' }); | ||
sdk.login({ username: "[email protected]", password: "test-secret" }); | ||
|
||
sdk.exchangeToken().then(res => { | ||
console.log('Trusted token: ', res.data); | ||
sdk.exchangeToken().then((res) => { | ||
console.log("Trusted token: ", res.data); | ||
}); | ||
|
||
``` | ||
## Seeing occasional 401 errors? | ||
|
@@ -137,12 +157,12 @@ Don't worry! That's part of the normal operations. | |
Flex API uses two authentication tokens for each session: | ||
* *Access token* that is used to authenticate the user. Valid only a | ||
- _Access token_ that is used to authenticate the user. Valid only a | ||
short amount of time. | ||
* *Refresh token* that is used to issue a fresh authentication | ||
- _Refresh token_ that is used to issue a fresh authentication | ||
token. Long lived. | ||
When *access token* expires, you will see a 401 error in browser's Web | ||
When _access token_ expires, you will see a 401 error in browser's Web | ||
Console. The SDK will handle this and automatically issue new fresh | ||
authentication token and retry the request. This all happens under the | ||
hood and you don't need to worry about it. SDK will do the heavy | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,15 @@ const createTokenStore = () => { | |
{ code: 'flex-authorization-code', username: '[email protected]' }, | ||
]; | ||
|
||
const knownIdpTokens = [ | ||
{ | ||
id: 'facebook', | ||
token: 'idp-token', | ||
clientId: 'idp-client-id', | ||
username: '[email protected]', | ||
}, | ||
]; | ||
|
||
// Private | ||
|
||
const generateAnonAccessToken = () => { | ||
|
@@ -106,6 +115,34 @@ const createTokenStore = () => { | |
return token.token; | ||
}; | ||
|
||
const createTokenWithIdp = (idpId, idpClientId, idpToken) => { | ||
const knownIdpToken = _.find( | ||
knownIdpTokens, | ||
({ id, token, clientId }) => id === idpId && token === idpToken && clientId === idpClientId | ||
); | ||
|
||
if (!knownIdpToken) { | ||
return null; | ||
} | ||
|
||
const { username } = knownIdpToken; | ||
const token = { | ||
token: { | ||
access_token: generateAccessToken(username), | ||
refresh_token: generateRefreshToken(username), | ||
token_type: 'bearer', | ||
expires_in: 86400, | ||
scope: 'user', | ||
}, | ||
user: { | ||
username, | ||
}, | ||
}; | ||
tokens.push(token); | ||
|
||
return token.token; | ||
}; | ||
|
||
const exchangeToken = accessToken => { | ||
const currentToken = _.find( | ||
tokens, | ||
|
@@ -182,6 +219,7 @@ const createTokenStore = () => { | |
createAnonToken, | ||
createTokenWithCredentials, | ||
createTokenWithAuthorizationCode, | ||
createTokenWithIdp, | ||
exchangeToken, | ||
freshToken, | ||
revokeRefreshToken, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/** | ||
Read `idpClientId` from `ctx` and add it to `params` | ||
Changes to `ctx`: | ||
- add `params.idpClientId` | ||
*/ | ||
export default class AddIdpClientIdToParams { | ||
enter({ params, ...ctx }) { | ||
const { idpClientId } = params; | ||
return { | ||
...ctx, | ||
idpClientId, | ||
params: { ...params, idp_client_id: idpClientId }, | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/** | ||
Read `idpId` from `ctx` and add it to `params` | ||
Changes to `ctx`: | ||
- add `params.idpId` | ||
*/ | ||
export default class AddIdpIdToParams { | ||
enter({ params, ...ctx }) { | ||
const { idpId } = params; | ||
return { | ||
...ctx, | ||
idpId, | ||
params: { ...params, idp_id: idpId }, | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/** | ||
Read `idpToken` from `ctx` and add it to `params` | ||
Changes to `ctx`: | ||
- add `params.idpToken` | ||
*/ | ||
export default class AddIdpTokenToParams { | ||
enter({ params, ...ctx }) { | ||
const { idpToken } = params; | ||
return { | ||
...ctx, | ||
idpToken, | ||
params: { ...params, idp_token: idpToken }, | ||
}; | ||
} | ||
} |
Oops, something went wrong.