import { BaseProvider } from '@ezbackend/auth' ; import { EzBackendInstance } from '@ezbackend/common' ; import { FastifyInstance , RouteOptions } from 'fastify' ; import fastifyPassport from 'fastify-passport' ; import { DeserializeFunction , SerializeFunction , } from 'fastify-passport/dist/Authenticator' ; import { Strategy as CustomStrategy } from 'passport-custom' ; import fastifyStatic from 'fastify-static' ; import Boom from '@hapi/boom' ; import path from 'path' ; import Web3Token from 'web3-token' ; export class Web3Provider extends BaseProvider { constructor ( modelName ) { super ( 'web3' , modelName ) ; } addStrategy ( instance , server , opts ) { const that = this ; server . register ( fastifyStatic , { root : path . join ( __dirname , '../web3-login-ui/build' ) , } ) ; const EthStrategy = new CustomStrategy ( async function ( req , callback ) { if ( ! req . query . token ) { throw Boom . badData ( ) ; } let recoveredAddress ; try { const { address , body } = await Web3Token . verify ( req . query . token ) ; recoveredAddress = address ; } catch { throw Boom . badData ( ) ; } const repo = instance . orm . getRepository ( that . modelName ) ; const model = { [ `${ that . providerName }Id` ] : recoveredAddress , [ `${ that . providerName }Data` ] : { } , } ; const serializedID = `${ that . providerName }-${ recoveredAddress }` ; repo . save ( model ) . then ( ( ) => { callback ( null , serializedID ) ; } , ( e ) => { if ( String ( e . driverError ) . toLowerCase ( ) . includes ( 'unique' ) ) { callback ( null , serializedID ) ; } else { callback ( e ) ; } } ) ; } ) ; return [ this . providerName , EthStrategy ] ; } getLoginRoute ( server , opts ) { const that = this ; return { method : 'GET' , url : `/${ this . getRoutePrefixNoPrePostSlash ( server ) }/login` , handler : ( req , res ) => { res . sendFile ( 'index.html' ) ; } , schema : { tags : [ `${ this . providerName } Auth` ] , summary : `Login for model '${ this . modelName }' with provider ${ this . providerName }` , description : `# 🔑 [CLICK HERE](/${ this . getFullRoutePrefixNoPrePostSlash ( server ) }/login) or visit the URL with this extension to login
                1. Creates/Updates '${ this . modelName }' on login
                2. Provider ${ this . providerName }` , } , } ; } getLogoutRoute ( server , opts ) { return { method : 'GET' , url : `/${ this . getRoutePrefixNoPrePostSlash ( server ) }/logout` , handler : function ( req , res ) { req . logOut ( ) . then ( ( ) => { res . redirect ( opts . successRedirectURL ) ; } ) ; } , schema : { tags : [ `${ this . providerName } Auth` ] , summary : `Logout for model '${ this . modelName }' with provider ${ this . providerName }` , description : `# 🔑 [CLICK HERE](/${ this . getFullRoutePrefixNoPrePostSlash ( server ) }/logout) or visit the URL with this extension to logout` , } , } ; } getCallbackRoute ( server , opts ) { const that = this ; return { method : 'GET' , url : `/${ this . getRoutePrefixNoPrePostSlash ( server ) }/callback` , preValidation : fastifyPassport . authenticate ( that . providerName ) , handler : function ( req , res ) { res . redirect ( opts . successRedirectURL ) ; } , schema : { tags : [ `${ this . providerName } Auth` ] , summary : `Callback Route for model '${ this . modelName }' with provider ${ this . providerName }` , description : `This route is not used for web3 login` , } , } ; } registerUserSerializer ( instance , opts ) { const that = this ; return async function serializer ( id , req ) { return id ; } ; } registerUserDeserializer ( instance , opts ) { const that = this ; return async function deserializer ( providerAndId , req ) { if ( providerAndId . startsWith ( `${ that . providerName }-` ) ) { const id = providerAndId . replace ( `${ that . providerName }-` , '' ) ; const userRepo = instance . orm . getRepository ( that . modelName ) ; const fullUser = await userRepo . findOne ( { [ `${ that . providerName }Id` ] : id , } ) ; if ( fullUser ) { return fullUser ; } else { return null ; } } else { throw 'pass' ; } } ; } }