From 7da925e00346acbd7a7d0cceef2db73be826186f Mon Sep 17 00:00:00 2001 From: hammadj Date: Thu, 12 Jan 2017 12:23:18 -0700 Subject: [PATCH 1/2] Simplify API using initWithClient, Refactor login response --- README.md | 16 +++++----- client/src/changePassword.js | 7 +++-- client/src/createUser.js | 45 ++++++++++++++------------- client/src/forgotPassword.js | 5 +-- client/src/index.js | 37 ++++++++++++++++++++-- client/src/loginWithPassword.js | 43 +++++++++++++------------ client/src/logout.js | 8 ++--- client/src/oauth/loginWithFacebook.js | 39 ++++++++++++----------- client/src/oauth/loginWithGoogle.js | 39 ++++++++++++----------- client/src/oauth/loginWithLinkedIn.js | 35 +++++++++++---------- client/src/resendVerificationEmail.js | 5 +-- client/src/resetPassword.js | 36 +++++++++++---------- client/src/store.js | 43 ++++++++++++++++++++----- client/src/verifyEmail.js | 33 +++++++++++--------- 14 files changed, 237 insertions(+), 154 deletions(-) diff --git a/README.md b/README.md index a2249ed..b955740 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Log the user in with a password. ```js import { loginWithPassword } from 'meteor-apollo-accounts' -loginWithPassword({username, email, password, plainPassword}, apollo) +loginWithPassword({username, email, password, plainPassword}) ``` - ```username```: Optional. The user's username. @@ -84,7 +84,7 @@ Change the current user's password. Must be logged in. ```js import { changePassword } from 'meteor-apollo-accounts' -changePassword({oldPassword, newPassword}, apollo) +changePassword({oldPassword, newPassword}) ``` - ```oldPassword```: The user's current password. This is not sent in plain text over the wire. @@ -112,7 +112,7 @@ Create a new user. ```js import { createUser } from 'meteor-apollo-accounts' -createUser({username, email, password, profile}, apollo) +createUser({username, email, password, profile}) ``` - ```username```: A unique name for this user. @@ -132,7 +132,7 @@ Marks the user's email address as verified. Logs the user in afterwards. ```js import { verifyEmail } from 'meteor-apollo-accounts' -verifyEmail({token}, apollo) +verifyEmail({token}) ``` - ```token```: The token retrieved from the verification URL. @@ -147,7 +147,7 @@ Request a forgot password email. ```js import { forgotPassword } from 'meteor-apollo-accounts' -forgotPassword({email}, apollo) +forgotPassword({email}) ``` - ```email```: The email address to send a password reset link. @@ -161,7 +161,7 @@ Reset the password for a user using a token received in email. Logs the user in ```js import { resetPassword } from 'meteor-apollo-accounts' -resetPassword({newPassword, token}, apollo) +resetPassword({newPassword, token}) ``` - ```newPassword```: A new password for the user. This is not sent in plain text over the wire. @@ -178,7 +178,7 @@ Logins the user with a facebook accessToken ```js import { loginWithFacebook } from 'meteor-apollo-accounts' -loginWithFacebook({accessToken}, apollo) +loginWithFacebook({accessToken}) ``` - ```accessToken```: A Facebook accessToken. It's recommended to use @@ -193,7 +193,7 @@ Logins the user with a google accessToken ```js import { loginWithGoogle } from 'meteor-apollo-accounts' -loginWithGoogle({accessToken}, apollo) +loginWithGoogle({accessToken}) ``` - ```accessToken```: A Google accessToken. It's recommended to use diff --git a/client/src/changePassword.js b/client/src/changePassword.js index 3836941..293f010 100644 --- a/client/src/changePassword.js +++ b/client/src/changePassword.js @@ -1,10 +1,11 @@ -import hashPassword from './hashPassword' import gql from 'graphql-tag' +import hashPassword from './hashPassword' +import {getClient} from './store' -export default async function ({oldPassword, newPassword}, apollo) { +export default async function ({oldPassword, newPassword}) { if (!oldPassword || !newPassword) throw new Error('Old and new password are required') - const result = await apollo.mutate({ + const result = await getClient().mutate({ mutation: gql`mutation changePassword($oldPassword: HashedPassword!, $newPassword: HashedPassword!) { changePassword(oldPassword: $oldPassword, newPassword: $newPassword) { success diff --git a/client/src/createUser.js b/client/src/createUser.js index 0269d3d..3acc98c 100644 --- a/client/src/createUser.js +++ b/client/src/createUser.js @@ -1,27 +1,30 @@ import hashPassword from './hashPassword' import gql from 'graphql-tag' -import {storeLoginToken} from './store' +import {handleLoginCallback, getClient} from './store' -export default async function ({username, email, password, profile}, apollo) { - const result = await apollo.mutate({ - mutation: gql` - mutation createUser ($username: String, $email: String, $password: HashedPassword!, $profile: CreateUserProfileInput) { - createUser (username: $username, email: $email, password: $password, profile: $profile) { - id - token - tokenExpires +export default async function ({username, email, password, profile}) { + let result + try { + result = await getClient().mutate({ + mutation: gql` + mutation createUser ($username: String, $email: String, $password: HashedPassword!, $profile: CreateUserProfileInput) { + createUser (username: $username, email: $email, password: $password, profile: $profile) { + id + token + tokenExpires + } } - } - `, - variables: { - username, - email, - password: hashPassword(password), - profile - } - }) + `, + variables: { + username, + email, + password: hashPassword(password), + profile + } + }) + } catch (err) { + return handleLoginCallback(err) + } - const {id, token, tokenExpires} = result.data.createUser - await storeLoginToken(id, token, new Date(tokenExpires)) - return id + return handleLoginCallback(null, result.data.createUser) } diff --git a/client/src/forgotPassword.js b/client/src/forgotPassword.js index 995beb7..68e6f18 100644 --- a/client/src/forgotPassword.js +++ b/client/src/forgotPassword.js @@ -1,7 +1,8 @@ import gql from 'graphql-tag' +import {getClient} from './store' -export default async function ({email}, apollo) { - const result = await apollo.mutate({ +export default async function ({email}) { + const result = await getClient().mutate({ mutation: gql`mutation forgotPassword($email: String!) { forgotPassword(email: $email) { success diff --git a/client/src/index.js b/client/src/index.js index fc41825..d0343d6 100644 --- a/client/src/index.js +++ b/client/src/index.js @@ -11,13 +11,19 @@ import loginWithFacebook from './oauth/loginWithFacebook' import loginWithGoogle from './oauth/loginWithGoogle' import loginWithLinkedIn from './oauth/loginWithLinkedIn' import userId from './userId' -import {onTokenChange, getLoginToken, setTokenStore} from './store' +import { + initWithClient, + setTokenStore, + TOKEN_EXPIRES_KEY, + TOKEN_KEY, + USER_ID_KEY, + onTokenChange +} from './store' export { changePassword, createUser, forgotPassword, - getLoginToken, hashPassword, loginWithPassword, logout, @@ -28,6 +34,31 @@ export { loginWithGoogle, loginWithLinkedIn, onTokenChange, + userId, + initWithClient, + setTokenStore, + TOKEN_EXPIRES_KEY, + TOKEN_KEY, + USER_ID_KEY, +} + +export default { + changePassword, + createUser, + forgotPassword, + loginWithPassword, + logout, + resendVerificationEmail, + resetPassword, + verifyEmail, + loginWithFacebook, + loginWithGoogle, + loginWithLinkedIn, + onTokenChange, + userId, + initWithClient, setTokenStore, - userId + TOKEN_EXPIRES_KEY, + TOKEN_KEY, + USER_ID_KEY, } diff --git a/client/src/loginWithPassword.js b/client/src/loginWithPassword.js index 33ae094..05f29af 100644 --- a/client/src/loginWithPassword.js +++ b/client/src/loginWithPassword.js @@ -1,26 +1,29 @@ import hashPassword from './hashPassword' import gql from 'graphql-tag' -import {storeLoginToken} from './store' +import {handleLoginCallback, getClient} from './store' -export default async function ({username, email, password}, apollo) { - const result = await apollo.mutate({ - mutation: gql` - mutation login ($username: String, $email: String, $password: HashedPassword!) { - loginWithPassword (username: $username, email: $email, password: $password) { - id - token - tokenExpires +export default async function ({username, email, password}) { + let result + try { + result = await getClient().mutate({ + mutation: gql` + mutation login ($username: String, $email: String, $password: HashedPassword!) { + loginWithPassword (username: $username, email: $email, password: $password) { + id + token + tokenExpires + } } - } - `, - variables: { - username, - email, - password: hashPassword(password) - } - }) + `, + variables: { + username, + email, + password: hashPassword(password) + } + }) + } catch (err) { + return handleLoginCallback(err) + } - const {id, token, tokenExpires} = result.data.loginWithPassword - await storeLoginToken(id, token, new Date(tokenExpires)) - return id + return handleLoginCallback(null, result.data.loginWithPassword) } diff --git a/client/src/logout.js b/client/src/logout.js index c8e5824..f2ade8d 100644 --- a/client/src/logout.js +++ b/client/src/logout.js @@ -1,8 +1,8 @@ -import {getLoginToken, resetStore} from './store' import gql from 'graphql-tag' +import {getLoginToken, resetStore, getClient} from './store' -export default async function (apollo) { - const result = await apollo.mutate({ +export default async function () { + const result = await getClient().mutate({ mutation: gql` mutation logout($token: String!) { logout(token: $token) { @@ -11,7 +11,7 @@ export default async function (apollo) { } `, variables: { - token: await getLoginToken() + token: getLoginToken() } }) diff --git a/client/src/oauth/loginWithFacebook.js b/client/src/oauth/loginWithFacebook.js index ec51a6f..b44ffe5 100644 --- a/client/src/oauth/loginWithFacebook.js +++ b/client/src/oauth/loginWithFacebook.js @@ -1,28 +1,31 @@ import gql from 'graphql-tag' -import {storeLoginToken} from '../store' +import {handleLoginCallback, getClient} from '../store' /** * Pass the accessToken * It's recommended to use https://github.com/keppelen/react-facebook-login */ -export default async function ({accessToken}, apollo) { - const result = await apollo.mutate({ - mutation: gql` - mutation loginWithFacebook ($accessToken: String!) { - loginWithFacebook (accessToken: $accessToken) { - id - token - tokenExpires +export default async function ({accessToken}) { + let result + try { + result = await getClient().mutate({ + mutation: gql` + mutation loginWithFacebook ($accessToken: String!) { + loginWithFacebook (accessToken: $accessToken) { + id + token + tokenExpires + } } - } - `, - variables: { - accessToken - } - }) + `, + variables: { + accessToken + } + }) + } catch (err) { + return handleLoginCallback(err) + } - const {id, token, tokenExpires} = result.data.loginWithFacebook - await storeLoginToken(id, token, new Date(tokenExpires)) - return id + return handleLoginCallback(null, result.data.loginWithFacebook) } diff --git a/client/src/oauth/loginWithGoogle.js b/client/src/oauth/loginWithGoogle.js index b0ffa61..12d659c 100644 --- a/client/src/oauth/loginWithGoogle.js +++ b/client/src/oauth/loginWithGoogle.js @@ -1,28 +1,31 @@ import gql from 'graphql-tag' -import {storeLoginToken} from '../store' +import {handleLoginCallback, getClient} from '../store' /** * Pass the accessToken * It's recommended to use https://github.com/anthonyjgrove/react-google-login */ -export default async function ({accessToken}, apollo) { - const result = await apollo.mutate({ - mutation: gql` - mutation loginWithGoogle ($accessToken: String!) { - loginWithGoogle (accessToken: $accessToken) { - id - token - tokenExpires +export default async function ({accessToken}) { + let result + try { + result = await getClient().mutate({ + mutation: gql` + mutation loginWithGoogle ($accessToken: String!) { + loginWithGoogle (accessToken: $accessToken) { + id + token + tokenExpires + } } - } - `, - variables: { - accessToken - } - }) + `, + variables: { + accessToken + } + }) + } catch (err) { + return handleLoginCallback(err) + } - const {id, token, tokenExpires} = result.data.loginWithGoogle - await storeLoginToken(id, token, new Date(tokenExpires)) - return id + return handleLoginCallback(null, result.data.loginWithGoogle) } diff --git a/client/src/oauth/loginWithLinkedIn.js b/client/src/oauth/loginWithLinkedIn.js index c8ea7d0..f136cdf 100644 --- a/client/src/oauth/loginWithLinkedIn.js +++ b/client/src/oauth/loginWithLinkedIn.js @@ -1,26 +1,29 @@ import gql from 'graphql-tag' -import {storeLoginToken} from '../store' +import {handleLoginCallback, getClient} from '../store' /** * Pass the accessToken * It's recommended to use https://github.com/keppelen/react-facebook-login */ -export default async function ({code, redirectUri}, apollo) { - const result = await apollo.mutate({ - mutation: gql` - mutation loginWithLinkedIn($code: String! $redirectUri: String!) { - loginWithLinkedIn(code: $code redirectUri: $redirectUri) { - id - token - tokenExpires +export default async function ({code, redirectUri}) { + let result + try { + result = await getClient().mutate({ + mutation: gql` + mutation loginWithLinkedIn($code: String! $redirectUri: String!) { + loginWithLinkedIn(code: $code redirectUri: $redirectUri) { + id + token + tokenExpires + } } - } - `, - variables: { code, redirectUri } - }) + `, + variables: { code, redirectUri } + }) + } catch (err) { + return handleLoginCallback(err) + } - const {id, token, tokenExpires} = result.data.loginWithLinkedIn - await storeLoginToken(id, token, new Date(tokenExpires)) - return id + return handleLoginCallback(null, result.data.loginWithLinkedIn) } diff --git a/client/src/resendVerificationEmail.js b/client/src/resendVerificationEmail.js index d3cd5f5..c9e94db 100644 --- a/client/src/resendVerificationEmail.js +++ b/client/src/resendVerificationEmail.js @@ -1,7 +1,8 @@ import gql from 'graphql-tag' +import {getClient} from './store' -export default async function ({email}, apollo) { - const result = await apollo.mutate({ +export default async function ({email}) { + const result = await getClient().mutate({ mutation: gql`mutation resendVerificationEmail($email: String) { resendVerificationEmail(email: $email) { success diff --git a/client/src/resetPassword.js b/client/src/resetPassword.js index d4b7e87..f2cc760 100644 --- a/client/src/resetPassword.js +++ b/client/src/resetPassword.js @@ -1,23 +1,27 @@ import gql from 'graphql-tag' -import {storeLoginToken} from './store' +import {handleLoginCallback, getClient} from './store' import hashPassword from './hashPassword' -export default async function ({newPassword, token}, apollo) { - const result = await apollo.mutate({ - mutation: gql`mutation resetPassword($newPassword: HashedPassword!, $token: String!) { - resetPassword(newPassword: $newPassword, token: $token) { - id +export default async function ({newPassword, token}) { + let result + try { + result = await getClient().mutate({ + mutation: gql` + mutation resetPassword($newPassword: HashedPassword!, $token: String!) { + resetPassword(newPassword: $newPassword, token: $token) { + id + token + tokenExpires + } + }`, + variables: { + newPassword: hashPassword(newPassword), token - tokenExpires } - }`, - variables: { - newPassword: hashPassword(newPassword), - token - } - }) + }) + } catch (err) { + return handleLoginCallback(err) + } - const {id, token: loginToken, tokenExpires} = result.data.resetPassword - await storeLoginToken(id, loginToken, new Date(tokenExpires)) - return id + return handleLoginCallback(null, result.data.resetPassword) } diff --git a/client/src/store.js b/client/src/store.js index ef3e04a..984c8c6 100644 --- a/client/src/store.js +++ b/client/src/store.js @@ -1,17 +1,22 @@ const onChangeCallbacks = [] +export const USER_ID_KEY = 'Meteor.userId' +export const TOKEN_KEY = 'Meteor.loginToken' +export const TOKEN_EXPIRES_KEY = 'Meteor.loginTokenExpires' + +let apollo let tokenStore = { set: async function ({userId, token, tokenExpires}) { - global.localStorage['Meteor.userId'] = userId - global.localStorage['Meteor.loginToken'] = token - global.localStorage['Meteor.loginTokenExpires'] = tokenExpires.toString() + global.localStorage[USER_ID_KEY] = userId + global.localStorage[TOKEN_KEY] = token + global.localStorage[TOKEN_EXPIRES_KEY] = tokenExpires.toString() }, get: async function () { return { - userId: global.localStorage['Meteor.userId'], - token: global.localStorage['Meteor.loginToken'], - tokenExpires: global.localStorage['Meteor.loginTokenExpires'] + userId: global.localStorage[USER_ID_KEY], + token: global.localStorage[TOKEN_KEY], + tokenExpires: global.localStorage[TOKEN_EXPIRES_KEY] } } } @@ -20,7 +25,29 @@ export const setTokenStore = function (newStore) { tokenStore = newStore } -export const storeLoginToken = async function (userId, token, tokenExpires) { +export const initWithClient = function (apolloClientInstance) { + apollo = apolloClientInstance +} + +export const getClient = function () { + if (!apollo) { + throw new Error('Meteor Apollo Accounts not initialized. Make sure you have called initWithClient(apollo).') + } + return apollo +} + +export const handleLoginCallback = async function (err, loginMethodResponse) { + if (!err) { // save user id and token + const {id, token, tokenExpires} = loginMethodResponse + await _storeLoginToken(id, token, new Date(tokenExpires)) + return id + } else { + resetStore() + return Promise.reject(err) + } +} + +export const _storeLoginToken = async function (userId, token, tokenExpires) { await tokenStore.set({userId, token, tokenExpires}) await tokenDidChange() } @@ -47,5 +74,5 @@ export const onTokenChange = function (callback) { } export const resetStore = async function () { - await storeLoginToken('', '', '') + await _storeLoginToken('', '', '') } diff --git a/client/src/verifyEmail.js b/client/src/verifyEmail.js index 5c9f3b8..2337d78 100644 --- a/client/src/verifyEmail.js +++ b/client/src/verifyEmail.js @@ -1,21 +1,24 @@ import gql from 'graphql-tag' -import {storeLoginToken} from './store' +import {handleLoginCallback, getClient} from './store' -export default async function ({token}, apollo) { - const result = await apollo.mutate({ - mutation: gql`mutation verifyEmail($token: String!) { - verifyEmail(token: $token) { - id +export default async function ({token}) { + let result + try { + result = await getClient().mutate({ + mutation: gql`mutation verifyEmail($token: String!) { + verifyEmail(token: $token) { + id + token + tokenExpires + } + }`, + variables: { token - tokenExpires } - }`, - variables: { - token - } - }) + }) + } catch (err) { + return handleLoginCallback(err) + } - const {id, token: loginToken, tokenExpires} = result.data.verifyEmail - await storeLoginToken(id, loginToken, new Date(tokenExpires)) - return id + return handleLoginCallback(null, result.data.verifyEmail) } From b250f5286cf0788e768b40a706db1b0346abb31a Mon Sep 17 00:00:00 2001 From: hammadj Date: Thu, 12 Jan 2017 12:30:59 -0700 Subject: [PATCH 2/2] Add login hooks and store userId in memory --- client/src/Events.js | 16 ++++++++ client/src/createUser.js | 5 ++- client/src/index.js | 15 +++++-- client/src/loginWithPassword.js | 5 ++- client/src/logout.js | 4 +- client/src/oauth/loginWithFacebook.js | 5 ++- client/src/oauth/loginWithGoogle.js | 5 ++- client/src/oauth/loginWithLinkedIn.js | 5 ++- client/src/store.js | 56 ++++++++++++++++++--------- 9 files changed, 88 insertions(+), 28 deletions(-) create mode 100644 client/src/Events.js diff --git a/client/src/Events.js b/client/src/Events.js new file mode 100644 index 0000000..b47e4df --- /dev/null +++ b/client/src/Events.js @@ -0,0 +1,16 @@ +export default { + _cbs: [], + notify (eventName) { + this._cbs.map(cb => { + if (cb.eventName === eventName && typeof cb.callback === 'function') { + cb.callback() + } + }) + }, + on (eventName, cb) { + this._cbs.push({ + eventName: eventName, + callback: cb + }) + } +} diff --git a/client/src/createUser.js b/client/src/createUser.js index 3acc98c..441340f 100644 --- a/client/src/createUser.js +++ b/client/src/createUser.js @@ -1,8 +1,9 @@ import hashPassword from './hashPassword' import gql from 'graphql-tag' -import {handleLoginCallback, getClient} from './store' +import {handleLoginCallback, getClient, startLoggingIn, endLoggingIn} from './store' export default async function ({username, email, password, profile}) { + startLoggingIn() let result try { result = await getClient().mutate({ @@ -24,6 +25,8 @@ export default async function ({username, email, password, profile}) { }) } catch (err) { return handleLoginCallback(err) + } finally { + endLoggingIn() } return handleLoginCallback(null, result.data.createUser) diff --git a/client/src/index.js b/client/src/index.js index d0343d6..9ba75a4 100644 --- a/client/src/index.js +++ b/client/src/index.js @@ -14,10 +14,13 @@ import userId from './userId' import { initWithClient, setTokenStore, + onLogin, + onLoginFailure, + onLogout, + loggingIn, TOKEN_EXPIRES_KEY, TOKEN_KEY, USER_ID_KEY, - onTokenChange } from './store' export { @@ -33,10 +36,13 @@ export { loginWithFacebook, loginWithGoogle, loginWithLinkedIn, - onTokenChange, userId, initWithClient, setTokenStore, + onLogin, + onLoginFailure, + onLogout, + loggingIn, TOKEN_EXPIRES_KEY, TOKEN_KEY, USER_ID_KEY, @@ -54,10 +60,13 @@ export default { loginWithFacebook, loginWithGoogle, loginWithLinkedIn, - onTokenChange, userId, initWithClient, setTokenStore, + onLogin, + onLoginFailure, + onLogout, + loggingIn, TOKEN_EXPIRES_KEY, TOKEN_KEY, USER_ID_KEY, diff --git a/client/src/loginWithPassword.js b/client/src/loginWithPassword.js index 05f29af..a8d8075 100644 --- a/client/src/loginWithPassword.js +++ b/client/src/loginWithPassword.js @@ -1,8 +1,9 @@ import hashPassword from './hashPassword' import gql from 'graphql-tag' -import {handleLoginCallback, getClient} from './store' +import {handleLoginCallback, getClient, startLoggingIn, endLoggingIn} from './store' export default async function ({username, email, password}) { + startLoggingIn() let result try { result = await getClient().mutate({ @@ -23,6 +24,8 @@ export default async function ({username, email, password}) { }) } catch (err) { return handleLoginCallback(err) + } finally { + endLoggingIn() } return handleLoginCallback(null, result.data.loginWithPassword) diff --git a/client/src/logout.js b/client/src/logout.js index f2ade8d..a25d6e3 100644 --- a/client/src/logout.js +++ b/client/src/logout.js @@ -1,5 +1,5 @@ import gql from 'graphql-tag' -import {getLoginToken, resetStore, getClient} from './store' +import {getLoginToken, handleLogout, getClient} from './store' export default async function () { const result = await getClient().mutate({ @@ -15,6 +15,6 @@ export default async function () { } }) - await resetStore() + await handleLogout() return result.data.logout.success } diff --git a/client/src/oauth/loginWithFacebook.js b/client/src/oauth/loginWithFacebook.js index b44ffe5..1ba5bd6 100644 --- a/client/src/oauth/loginWithFacebook.js +++ b/client/src/oauth/loginWithFacebook.js @@ -1,5 +1,5 @@ import gql from 'graphql-tag' -import {handleLoginCallback, getClient} from '../store' +import {handleLoginCallback, getClient, startLoggingIn, endLoggingIn} from '../store' /** * Pass the accessToken @@ -7,6 +7,7 @@ import {handleLoginCallback, getClient} from '../store' */ export default async function ({accessToken}) { + startLoggingIn() let result try { result = await getClient().mutate({ @@ -25,6 +26,8 @@ export default async function ({accessToken}) { }) } catch (err) { return handleLoginCallback(err) + } finally { + endLoggingIn() } return handleLoginCallback(null, result.data.loginWithFacebook) diff --git a/client/src/oauth/loginWithGoogle.js b/client/src/oauth/loginWithGoogle.js index 12d659c..9c2d149 100644 --- a/client/src/oauth/loginWithGoogle.js +++ b/client/src/oauth/loginWithGoogle.js @@ -1,5 +1,5 @@ import gql from 'graphql-tag' -import {handleLoginCallback, getClient} from '../store' +import {handleLoginCallback, getClient, startLoggingIn, endLoggingIn} from '../store' /** * Pass the accessToken @@ -7,6 +7,7 @@ import {handleLoginCallback, getClient} from '../store' */ export default async function ({accessToken}) { + startLoggingIn() let result try { result = await getClient().mutate({ @@ -25,6 +26,8 @@ export default async function ({accessToken}) { }) } catch (err) { return handleLoginCallback(err) + } finally { + endLoggingIn() } return handleLoginCallback(null, result.data.loginWithGoogle) diff --git a/client/src/oauth/loginWithLinkedIn.js b/client/src/oauth/loginWithLinkedIn.js index f136cdf..911234e 100644 --- a/client/src/oauth/loginWithLinkedIn.js +++ b/client/src/oauth/loginWithLinkedIn.js @@ -1,5 +1,5 @@ import gql from 'graphql-tag' -import {handleLoginCallback, getClient} from '../store' +import {handleLoginCallback, getClient, startLoggingIn, endLoggingIn} from '../store' /** * Pass the accessToken @@ -7,6 +7,7 @@ import {handleLoginCallback, getClient} from '../store' */ export default async function ({code, redirectUri}) { + startLoggingIn() let result try { result = await getClient().mutate({ @@ -23,6 +24,8 @@ export default async function ({code, redirectUri}) { }) } catch (err) { return handleLoginCallback(err) + } finally { + endLoggingIn() } return handleLoginCallback(null, result.data.loginWithLinkedIn) diff --git a/client/src/store.js b/client/src/store.js index 984c8c6..b31ff4a 100644 --- a/client/src/store.js +++ b/client/src/store.js @@ -1,9 +1,12 @@ +import Events from './Events' -const onChangeCallbacks = [] export const USER_ID_KEY = 'Meteor.userId' export const TOKEN_KEY = 'Meteor.loginToken' export const TOKEN_EXPIRES_KEY = 'Meteor.loginTokenExpires' +let _tokenSaved +let _userIdSaved +let _isLoggingIn = true let apollo let tokenStore = { @@ -18,6 +21,11 @@ let tokenStore = { token: global.localStorage[TOKEN_KEY], tokenExpires: global.localStorage[TOKEN_EXPIRES_KEY] } + }, + remove: async function () { + global.localStorage.removeItem(USER_ID_KEY) + global.localStorage.removeItem(TOKEN_KEY) + global.localStorage.removeItem(TOKEN_EXPIRES_KEY) } } @@ -40,39 +48,51 @@ export const handleLoginCallback = async function (err, loginMethodResponse) { if (!err) { // save user id and token const {id, token, tokenExpires} = loginMethodResponse await _storeLoginToken(id, token, new Date(tokenExpires)) + _tokenSaved = token + _userIdSaved = id + Events.notify('onLogin') return id } else { - resetStore() + Events.notify('onLoginFailure') + handleLogout() return Promise.reject(err) } } +export const handleLogout = async function () { + await tokenStore.remove() + _tokenSaved = null + _userIdSaved = null + Events.notify('onLogout') +} + export const _storeLoginToken = async function (userId, token, tokenExpires) { await tokenStore.set({userId, token, tokenExpires}) - await tokenDidChange() } -export const getLoginToken = async function () { - const {token} = await tokenStore.get() - return token +export const loggingIn = function () { + return _isLoggingIn } -export const getUserId = async function () { - const {userId} = await tokenStore.get() - return userId +export const onLogin = function (cb) { + Events.on('onLogin', cb) } -const tokenDidChange = async function () { - const newData = await tokenStore.get() - for (const callback of onChangeCallbacks) { - callback(newData) - } +export const onLoginFailure = function (cb) { + Events.on('onLoginFailure', cb) } -export const onTokenChange = function (callback) { - onChangeCallbacks.push(callback) +export const onLogout = function (cb) { + Events.on('onLogout', cb) } -export const resetStore = async function () { - await _storeLoginToken('', '', '') +export const startLoggingIn = function () { + _isLoggingIn = true } + +export const endLoggingIn = function () { + _isLoggingIn = false +} + +export const getLoginToken = () => _tokenSaved +export const getUserId = () => _userIdSaved