|
// --- path: src/auth/SessionContext.js |
|
import _ /**/ from 'lodash' |
|
import React /**/ from 'react' |
|
import { Auth } from 'aws-amplify' |
|
// |
|
import { ALLOW, ERRORED, UNSET, VALID } from '../utils/Symbols' |
|
import say from '../utils/say' |
|
import * as Errors from '../utils/Errors' |
|
import * as UF from '../utils/Useful' |
|
import * as Log from '../utils/Log' |
|
import * as IDHelpers from '../utils/IDHelpers' |
|
|
|
export const SessionContext = React.createContext(null) |
|
|
|
export function getSessionUpdaters({ setSession, setAuthStatus, setIsOnboarded }) { |
|
const erroredSession = async (err, story = {}) => { |
|
try { |
|
setSession(null) |
|
setAuthStatus(ERRORED) |
|
return Log.error(err, story) |
|
} catch (err2) { |
|
console.error('double error in erroredSession') |
|
return err2 |
|
} |
|
} |
|
const unsetSession = async (story = {}) => { |
|
setSession(null) |
|
setAuthStatus(UNSET) |
|
return Log.info('unsetSession', story) |
|
} |
|
const setValidSession = async (cogsession, story = {}) => { |
|
try { |
|
if (isValidCogsession) { |
|
say.sess('app', 'setValidSession', cogsession, story) |
|
const session = parseCogession(cogsession, story) |
|
// |
|
setSession(session) |
|
setAuthStatus(VALID) |
|
await Log.startSession(session) |
|
return session |
|
} |
|
await Promise.all([ |
|
Log.warn('setValidSession with invalid session', { ...story, cogsession, during: 'setValidSession' }), |
|
unsetSession(story), |
|
]) |
|
} catch (err) { |
|
erroredSession(err, { ...story, during: 'setValidSession' }) |
|
} |
|
return null |
|
} |
|
// |
|
const logout = async (options = {}, storyIn = {}) => { |
|
const { global = false, otherTasks = [] } = options |
|
const story = { ...storyIn, ...options, during: 'logout' } |
|
await Promise.all([ |
|
unsetSession(story), |
|
Auth.signOut({ global }), |
|
..._.map(otherTasks, (task) => task(options, storyIn)), |
|
]).catch((err) => { erroredSession(err, story) }) |
|
say.hi('auth', 'logged out') |
|
} |
|
// |
|
const refreshSession = async (story = {}) => ( |
|
Auth.currentAuthenticatedUser().then(async (session) => { |
|
if (session) { |
|
say.sess('app', 'get auth', story, session) |
|
const result = await setValidSession(session, story) |
|
return result |
|
} |
|
say.sess('app', 'no user', story, session) |
|
await unsetSession(story) |
|
return null |
|
}).catch(async (err) => { |
|
if (/The user is not authenticated/.test(err)) { // WTH Amazon Cognito is THE WORST |
|
await unsetSession(story).catch((err2) => { console.error('double error in sessionRefresh', err, err2); return null }) |
|
return null |
|
} |
|
if (err.code) { |
|
Log.error(err, { during: 'app auth error', ...story }) |
|
await unsetSession(story).catch((err2) => { console.error('double error in sessionRefresh', err, err2); return null }) |
|
return null |
|
} |
|
Log.error(err, { during: 'app session refresh error', ...story }) |
|
await erroredSession(err, story) |
|
.catch((err2) => { console.error('double error in sessionRefresh', err, err2) }) |
|
throw err |
|
}) |
|
) |
|
// |
|
return { refreshSession, logout, setValidSession, unsetSession, erroredSession, setIsOnboarded } |
|
} |
|
|
|
export function isValidCogsession(cogsession) { |
|
return (!! ( |
|
cogsession?.signInUserSession?.idToken?.jwtToken |
|
&& cogsession?.username |
|
&& cogsession?.attributes?.email |
|
)) |
|
} |
|
|
|
export function parseCogession(cogsession, story = {}) { |
|
// skipped: username, pool, Session, client, authenticationFlowType, storage, pool, keyPrefix, userDataKey, attributes, preferredMFA |
|
// |
|
if (! isValidCogsession(cogsession)) { |
|
throw Errors.BadValue({ val: { cogsession }, want: 'raw cognito session' }) |
|
} |
|
// |
|
const { username:cogkey, attributes:rawattrs, signInUserSession:tokens } = cogsession |
|
const jwt = tokens.idToken.jwtToken |
|
const cogattrs = parseCogattrs(rawattrs) |
|
// |
|
const session = { ...cogattrs, cogkey } |
|
// sugar to set a non-enumerable property |
|
UF.adorn(session, 'cogsession', cogsession) |
|
UF.adorn(session, 'jwt', jwt) |
|
UF.adorn(session, 'tokens', tokens) |
|
// say.hi('auth', 'session', session, session.jwt, session.tokens) |
|
// |
|
say.hi('SessionContext', 'parse cogsession', story, session) |
|
return session |
|
} |
|
|
|
// export const isManagerish = () => { |
|
// const activeRole = getActiveRole().toLowerCase() |
|
// return false |
|
// } |
|
|
|
const GUEST_ABILITIES = { |
|
addToBasket: ALLOW, |
|
} |
|
|
|
const EDITOR_ABILITIES = { |
|
addContact: ALLOW, addLoc: ALLOW, editPurchase: ALLOW, editChat: ALLOW, |
|
usePicker: ALLOW, addSemaphore: ALLOW, |
|
} |
|
|
|
const MANAGER_ABILITIES = { |
|
placePurchase: ALLOW, editTeam: ALLOW, |
|
} |
|
|
|
function getAbilities(session) { |
|
const { claimedrole } = session |
|
const managerish = /^(owner|manager|admin)$/.test(claimedrole) |
|
const editorish = /^(owner|manager|admin|editor)$/.test(claimedrole) |
|
const guestish = true |
|
const abilities = {} |
|
if (managerish) { Object.assign(abilities, MANAGER_ABILITIES) } |
|
if (editorish) { Object.assign(abilities, EDITOR_ABILITIES) } |
|
if (guestish) { Object.assign(abilities, GUEST_ABILITIES) } |
|
return abilities |
|
} |
|
|
|
export function parseCogattrs(rawattrs) { |
|
try { |
|
const { |
|
email_verified:emailVerified, |
|
phone_number_verified:phoneVerified, |
|
locale, |
|
'custom:orgs':orgRolesJSON, |
|
"custom:activeOrg":activeOrgJSON, |
|
email: cogemail, |
|
} = rawattrs || {} |
|
// |
|
const orgRoles = orgRolesJSON ? JSON.parse(orgRolesJSON) : {} |
|
if (_.isEmpty(orgRoles)) { Log.warn('No Org Roles found!', { during: 'parseCogattrs', rawattrs }) } |
|
const okeys = _.keys(orgRoles) |
|
// |
|
const activeOrg = JSON.parse(activeOrgJSON) |
|
// |
|
const { okey:activeOkey, id:activeOrgID } = activeOrg |
|
const identkey = IDHelpers.identkeyFromEmail0({ email0: cogemail }) |
|
const identID = `dnt.tk:${identkey}` |
|
const identingID = `dng.tk/${identID}/${activeOrgID}` |
|
const claimedrole = orgRoles?.[activeOkey]?.role |
|
// |
|
const session = { |
|
cogemail, identkey, claimedrole, identID, identingID, activeOkey, activeOrgID, orgRoles, okeys, emailVerified, phoneVerified, locale, |
|
} |
|
UF.requireValues({ cogemail, identkey, claimedrole, identID, identingID, activeOkey, activeOrgID, orgRoles, okeys, emailVerified }) |
|
// |
|
session.abilities = getAbilities(session) |
|
session.can = (ability) => (session.abilities[ability] === ALLOW) |
|
return session |
|
// |
|
} catch (err) { |
|
Log.error(err, { during: 'parseCogattrs', rawattrs }) |
|
throw err |
|
} |
|
} |
|
|
|
SessionContext.getSessionUpdaters = getSessionUpdaters |
|
export default SessionContext |