import jwt from 'jsonwebtoken' ; import getIn from 'lodash/get' ; import getIntersection from 'lodash/intersection' ; import isEmpty from 'lodash/isEmpty' ; import { Request } from 'express' ; import * as vars from './vars' ; import { generateClaimsJwtToken , generateJwtRefreshToken } from './auth-tools' ; import { User , createUserSession , createUserAccount , getUserById , changeUserPassword , activateUser , getUserByRefreshToken , getUserByCredentials , } from './hasura' ; import { isUserAllowedToChangePassword } from './custom-logic' ; const getRole = ( req ) => getIn ( req , `headers["${ vars . hasuraHeaderPrefix }role"]` , '' ) ; const isAdmin = ( req ) => getRole ( req ) === 'admin' ; const getDataFromVerifiedAuthorizationToken = ( req ) : undefined | any => { const { authorization } = req . headers ; if ( authorization === undefined ) { return void 0 ; } const token = authorization . replace ( 'Bearer ' , '' ) ; try { return jwt . verify ( token , vars . jwtSecretKey ) ; } catch ( e ) { return void 0 ; } } ; const getFieldFromDataAuthorizationToken = ( req , field ) => { const verifiedToken = getDataFromVerifiedAuthorizationToken ( req ) ; return getIn ( verifiedToken , `["${ vars . hasuraGraphqlClaimsKey }"]${ vars . hasuraHeaderPrefix }${ field }` ) ; } ; const getCurrentUserId = ( req ) => getFieldFromDataAuthorizationToken ( req , 'user-id' ) ; const isAuthenticated = ( req ) : boolean => { return ! ! getDataFromVerifiedAuthorizationToken ( req ) ; } ; const checkUserCanDoRegistration = ( req ) : boolean => { if ( isAdmin ( req ) ) { return true ; } if ( vars . allowRegistrationFor !== '*' ) { const allowedRoles = vars . allowRegistrationFor . split ( ',' ) ; return allowedRoles . includes ( getRole ( req ) ) ; } return true ; } ; const checkUserIsPartOfStaffOrIsTheCurrentUser = async ( req , user ) : Promise < boolean > => { if ( isAdmin ( req ) ) { return true ; } const roles = getIn ( req , `headers["${ vars . hasuraHeaderPrefix }role"]` , '' ) . split ( ',' ) . map ( ( role ) => role . trim ( ) ) ; const currentUserId = getCurrentUserId ( req ) ; if ( getIntersection ( roles , [ 'admin' ] ) . length >= 1 || ( await isUserAllowedToChangePassword ( currentUserId , user , req ) ) ) { return true ; } if ( ! isAuthenticated ( req ) ) { return false ; } return currentUserId === user . id ; } ; const resolvers = { Query : { async auth_me ( _ , args , ctx ) { if ( ! isAuthenticated ( ctx . req ) ) { throw new Error ( 'Authorization token has not provided' ) ; } try { const currentUserId = getCurrentUserId ( ctx . req ) ; return getUserById ( currentUserId ) ; } catch ( e ) { throw new Error ( 'Not logged in' ) ; } } , } , Mutation : { async auth_login ( _ , { organization_id , email , password } , ctx ) { if ( ! vars . allowEmptyOrganization && ! organization_id ) { throw new Error ( 'Missing organization_id' ) ; } const user = await getUserByCredentials ( organization_id , email , password ) ; const ipAddress = ( ctx . req . headers [ 'x-forwarded-for' ] || ctx . req . connection . remoteAddress || '' ) . split ( ',' ) [ 0 ] . trim ( ) ; const [ refreshToken , sessionId ] = await createUserSession ( user , ctx . req . headers [ 'user-agent' ] , ipAddress ) ; const accessToken = generateClaimsJwtToken ( user , sessionId ) ; return { access_token : accessToken , refresh_token : generateJwtRefreshToken ( { token : refreshToken , } ) , organization_id : user . organization_id , user_id : user . id , } ; } , async auth_register ( _ , { organization_id , email , password } , ctx ) { if ( ! vars . allowEmptyOrganization && ! organization_id ) { throw new Error ( 'Missing organization_id' ) ; } if ( ! checkUserCanDoRegistration ( ctx . req ) ) { throw new Error ( 'Forbidden' ) ; } const user = await createUserAccount ( organization_id , email , password ) ; return { affected_rows : Number ( user !== undefined ) , } ; } , async auth_change_password ( _ , { user_id , new_password } , ctx ) { const user = await getUserById ( user_id ) ; if ( ! user ) { throw new Error ( 'Unable to find user' ) ; } if ( ! ( await checkUserIsPartOfStaffOrIsTheCurrentUser ( ctx . req , user ) ) ) { throw new Error ( 'Forbidden' ) ; } const result = await changeUserPassword ( user , new_password ) ; return { affected_rows : Number ( result ) , } ; } , async auth_activate_account ( _ , { organization_id , email , secret_token } ) { if ( ! vars . allowEmptyOrganization && ! organization_id ) { throw new Error ( 'Missing organization_id' ) ; } if ( isEmpty ( email ) ) { throw new Error ( 'Invalid email' ) ; } if ( isEmpty ( secret_token ) ) { throw new Error ( 'Invalid secret_token' ) ; } const result = await activateUser ( organization_id , email , secret_token ) ; return { affected_rows : Number ( result ) , } ; } , async auth_refresh_token ( _ , { refresh_token } , ctx ) { if ( ! refresh_token ) { throw new Error ( 'Refresh token has not provided' ) ; } const refreshTokenPayload = jwt . verify ( refresh_token , vars . jwtSecretKey ) ; const user = await getUserByRefreshToken ( refreshTokenPayload . token ) ; const ipAddress = ( ctx . req . headers [ 'x-forwarded-for' ] || ctx . req . connection . remoteAddress || '' ) . split ( ',' ) [ 0 ] . trim ( ) ; const [ newRefreshToken , sessionId ] = await createUserSession ( user , ctx . req . headers [ 'user-agent' ] , ipAddress ) ; const accessToken = generateClaimsJwtToken ( user , sessionId ) ; return { access_token : accessToken , refresh_token : generateJwtRefreshToken ( { token : newRefreshToken , } ) , organization_id : user . organization_id , user_id : user . id , } ; } , } , } ; export default resolvers ;