This guide demonstrate how to create an externally-owned account using your email or social media account. Once authenticated, you can sign transactions and interact with any Safe Smart Accounts you own.


Install dependencies

yarn add @safe-global/auth-kit @web3auth/base @web3auth/modal @web3auth/openlogin-adapter

Create a Web3AuthModalPack instance

We are going to use the provided Web3AuthModalPack exported in the @safe-global/auth-kit package.
Create an instance of the Web3AuthModalPack using the required Web3AuthConfig configuration object.
import { Web3AuthModalPack, Web3AuthConfig } from '@safe-global/auth-kit'
import { Web3AuthOptions } from '@web3auth/modal'
import { OpenloginAdapter } from '@web3auth/openlogin-adapter'
const options: Web3AuthOptions = {
clientId: 'YOUR_WEB3_AUTH_CLIENT_ID', //
web3AuthNetwork: 'testnet',
chainConfig: {
chainNamespace: CHAIN_NAMESPACES.EIP155,
chainId: '0x5',
rpcTarget: ''
uiConfig: {
theme: 'dark',
loginMethodsOrder: ['google', 'facebook']
const modalConfig = {
label: 'torus',
showOnModal: false
label: 'metamask',
showOnDesktop: true,
showOnMobile: false
const openloginAdapter = new OpenloginAdapter({
loginSettings: {
mfaLevel: 'mandatory'
adapterSettings: {
uxMode: 'popup',
whiteLabel: {
name: 'Safe'
const web3AuthConfig: Web3AuthConfig = {
txServiceUrl: ''
// Instantiate and initialize the pack
const web3AuthModalPack = new Web3AuthModalPack(web3AuthConfig)
await web3AuthModalPack.init({ options, adapters: [openloginAdapter], modalConfig })

Sign in to an Ethereum account

Once your Web3AuthModalPack instance is created, use the signIn() method to start the authentication process. Usually, you call this method when the user clicks on a "Sign In" button added to your page.
Important considerations about Web3Auth are:
  1. 1.
    When you sign in with the same social account, the same Ethereum address will be returned for the same Web3Auth client ID. Web3Auth scopes the creation of the wallet (address) to the DApp, so when interacting with other DApps using Web3Auth, a different Ethereum address will be returned. This is by design and to enhanced security.
  2. 2.
    If you sign in with an email and then with a social account using the same email (e.g. "Sign in with Google"), a different Ethereum address might be returned even the same email address is used.
// The signIn() method will return the user's Ethereum address
// The await will last until the user is authenticated, so while the UI modal is showed
const authKitSignData = await web3AuthModalPack.signIn()
The returned authKitSignData data contains the following props:
AuthKitSignInData {
eoa: string // The safe signer
safes?: string[] // The list of associated Safe addresses
The signOut() method removes the current session.
await web3AuthModalPack.signOut()
Call getProvider() to get the Ethereum provider instance.
We expose two methods for listening to events, subscribe() and unsubscribe(). In the Web3AuthModalPack case, we can listen to all the events listed here.
import { ADAPTER_EVENTS } from '@web3auth/base'
web3AuthModalPack.subscribe(ADAPTER_EVENTS.CONNECTED, () => {
console.log('User is authenticated')
web3AuthModalPack.subscribe(ADAPTER_EVENTS.DISCONNECTED, () => {
console.log('User is not authenticated')
When txServiceUrl is provided in the Web3AuthModalPack instantiation, the list of associated Safe addresses will be returned as part of the signIn() method response.
const web3AuthModalPack = new Web3AuthModalPack({
txServiceUrl: ''

Signing transactions using the Web3AuthModalPack and Protocol Kit

The Web3AuthModalPack can be combined with the Protocol Kit to connect to a Safe using the provider and signer of the currently authenticated account.
Once connected, you can use any of the methods available in the Protocol Kit.
import { ethers } from 'ethers'
import { EthersAdapter } from '@safe-global/protocol-kit'
provider = new ethers.providers.Web3Provider(web3AuthModalPack.getProvider())
signer = provider.getSigner()
const ethAdapter = new EthersAdapter({
signerOrProvider: signer || provider
const safeSDK = await Safe.create({
// Create a Safe transaction with the provided parameters
const safeTransactionData: MetaTransactionData = {
to: '0x',
data: '0x',
value: ethers.utils.parseUnits('0.0001', 'ether').toString()
const safeTransaction = await safeSDK.createTransaction({ safeTransactionData })

Sign messages using the Web3AuthModalPack

You can also sign any arbitrary message or transaction as a regular Signing Account with your favorite web3 library:
// Using web3
const web3 = new Web3(web3AuthModalPack.getProvider())
await web3.eth.sendTransaction(tx)
await web3.eth.signTransaction(tx)
const message = 'hello world'
const address = '0x...'
await web3.eth.personal.sign(message, address)
// Using ethers
const provider = new ethers.providers.Web3Provider(web3AuthModalPack.getProvider())
const signer = provider.getSigner()
await signer.sendTransaction(tx)
await signer.signTransaction(tx)
await signer.signMessage(message)

Alternative example in @safe-global/safe-core-sdk

See an example on how to initialize and use the Auth Kit.