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) }