Integrate AWS Cognito with Web3Auth
This guide will cover the basics of how to set up your Web3Auth SDK and AWS Cognito for the integration and provide you with the links on how to develop a basic web application on the Ethereum blockchain.
Quick Start
npx degit Web3Auth/web3auth-pnp-examples/web-no-modal-sdk/custom-authentication/single-verifier-examples/cognito-no-modal-example w3a-cognito-demo && cd w3a-cognito-demo && npm install && npm run start
How it works?
- Implicit Flow
- Authorization Code Flow
When integrating Web3Auth with AWS Cognito the Implicit flow looks something like this:
When integrating Web3Auth Plug and Play No Modal SDK with AWS Cognito the Authorization code flow looks something like this:
For Authorization code flow, you will need an additional backend server to communicate with AWS
Cognito to get the id_token
in exchange for authorization_code
received.
-
When a user logs in with AWS Cognito, Cognito sends a JWT
id_token
to the app. This JWT token is sent to the Web3Auth SDK's login function. -
Finally, on successful validation of the JWT token, Web3Auth SDK will generate a private key for the user, in a self-custodial way, resulting in easy onboarding for your user to the application.
Prerequisites
-
For Web Apps: A basic knowledge of JavaScript is required to use Web3Auth SDK.
-
For Mobile Apps: For the Web3Auth Mobile SDKs, you have a choice between iOS, Android, React Native & Flutter. Please refer to the Web3Auth SDK Reference for more information.
-
Create a Web3Auth account on the Web3Auth Dashboard
-
An AWS account to create an AWS Cognito User Poll.
-
A Google Developer account to be used as an Identity provider for AWS Cognito.
Setup
Set up AWS Cognito
-
Go to your AWS account and go to AWS Cognito console. And Create a new user pool by following this AWS Console guide.
-
Note down the
userPoolId
andregion
after creating the new user pool. -
Add a new app client to the user pool from the App clients tab under General settings of your pool settings. Note down the
App client id
for this app client, which looks something like3j2tXXXXkkff5ajpn
. We will use this as theaud
value for the JWT validation field. -
Add a domain to your AWS Cognito App from the
App integration > Domain name
section. -
We will use Google login as an Identity provider in AWS Cognito for this guide.
-
Obtain the OAuth
Client ID
andClient Secret
from your Google Developer dashboard. Follow Google’s instructions to set up an OAuth 2.0 App. -
While configuring your Google OAuth 2.0 client for the web, make sure to enter
<AWS_COGNITO_DOMAIN>
in theAuthorized Javascript Origins list
and,<AWS_COGNITO_DOMAIN>/oauth2/idpresponse
in the Authorized redirect URIs list.
-
-
Let's configure Google login in AWS Cognito.
- Go to the identity providers tab under the federation tab and select Google.
- It will require you to enter your Google app's
Client ID
andClient Secret
which you get in the above steps.- Paste
Client ID
in the Google app ID field. - Paste
Client Secret
in the App secret field. - Type
profile email openid
in the Authorize scope field. - Your Google login provider is configured, you can enable it in your App client settings under App Integration settings of your pool settings.
- Map email from Google attribute to user pool attribute.
- In the Amazon Cognito console, choose Manage user pools, and then choose your user pool.
- In the left navigation pane, under Federation, choose
Attribute mapping
. - On the attribute mapping page, choose the
Google
tab. - Next to the Google attribute named
email
, select the Capture check box. - Next to email, for the User pool attribute, choose
Email
from the list. - Choose Save changes.
- Paste
-
Update App client settings to set the desired OAuth flow and redirect endpoints. For this guide, we will be using the Implicit OAuth flow.
- Click on
App client settings
under App integration. - Callback URL(s):
http://localhost:3000/callback, https://testing.openlogin.com/auth
- Sign out URL(s):
http://localhost:3000
- Additional to your own URLs for the application, please add
- https://testing.openlogin.com/auth (for Web3Auth testnet used in demo applications) and
- https://auth.web3auth.io/auth (for Web3Auth mainnet used in production applications).
- Additional to your own URLs for the application, please add
- OAuth2.0
- Select [x]
Implicit grant
from Allowed OAuth Flows - Select [x]
email
openid
profile
from Allowed OAuth Scopes
- Select [x]
- Save Changes and your Cognito app is configured to use Web3Auth.
- Click on
-
Now let's configure Web3Auth using AWS Cognito app details.
Setup Web3Auth
Setup Web3Auth Project
-
Create a Project from the Project Section of the Web3Auth Developer Dashboard.
-
Enter your desired Project name.
-
Select the Product you want to use. For this guide, we'll be using the Plug n Play product.
-
Select the Platform type you want to use. For this guide, we'll be using the Web Application as the platform.
-
Select the Web3Auth Network as
Sapphire Devnet
. We recommend creating a project in thesapphire_devnet
network during development. While moving to a production environment, make sure to convert your project tosapphire_mainnet
or any of the legacy mainnet networkmainnet
,aqua
, orcyan
network. Otherwise, you'll end up losing users and keys. -
Select the blockchain(s) you'll be building this project on. For interoperability with Torus Wallets, you have the option of allowing the user's private key to be used in other applications using Torus Wallets (EVM, Solana, XRPL & Casper).
-
Finally, once you create the project, you have the option to whitelist your URLs for the project. Please whitelist the domains where your project will be hosted.
-
Setup Firebase Custom Authentication Verifier
Create a AWS Cognito Verifier from the Custom Authentication tab of your Web3Auth Project.
-
Click on the
Custom Authentication
tab of your Web3Auth Project. -
Click on the
Create Verifier
button. -
Enter a name of your choice for the verifier identifier.
eg. w3a-cognito-demo
-
Select
Custom Providers
from Choose a Login Provider section. -
JWKS Endpoint: Enter
https://cognito-idp.{REGION}.amazonaws.com/{USER_POOL_ID}/.well-known/jwks.json
as the JWKS endpoint for the Cognito. -
Now you have the option to paste a sample idToken(JWT) to get the fields for the JWT validation. This step is optional, but if you have a sample JWT you can paste it here to get the fields for the JWT validation. You can also skip this step and fill in the fields manually.
-
The following are the JWT validation fields needed for the Cognito JWT validation:
- Type
iss
as a field andhttps://cognito-idp.{REGION}.amazonaws.com/{USER_POOL_ID}
as a value. - Next, type
aud
as a field andAPP_CLIENT_ID
as a value.
Note: Replace the
REGION
,USER_POOL_ID
andAPP_CLIENT_ID
with your Cognito specific details. - Type
-
Next, Select
Sub
,Email
or aCustom
value from the dropdown for the JWT Verifier ID. This is the field that will be used as the verifier ID for the user, and it has to be unique for each user. -
Finally, Click on the
Create
button to create your verifier.
It typically takes 5-10 minutes for the verifier to go live. Once deployed & live, you'll receive an email and the dashboard will display the 'Live' status for the verifier.
Using the Web3Auth SDK
To use the Web3Auth SDK, you need to add the dependency of the respective platform SDK of Web3Auth to your project. To know more about the available SDKs, please have a look at this documentation page.
For this guide, we will be talking through the Web3Auth Plug and Play No Modal SDK and using the Auth Adapter alongside it to enable Custom Authentication through AWS Cognito.
Setting up your base project for using Web3 libraries:
If you are starting from scratch, to set up this project locally, you will need to create a base Web application, where you can install the required dependencies. However, while working with Web3, there are a few base libraries, which need additional configuration. This is because certain packages are not available in the browser environment, and we need to polyfill them manually. You can follow this documentation where we have mentioned the configuration changes for some popular frameworks for your reference.
Installation
For this project, you need to add the following Web3Auth dependencies to your package.json
npm install --save @web3auth/no-modal @web3auth/auth-adapter @web3auth/ethereum-provider @web3auth/base web3
Understanding the Dependencies
- Web3Auth Dependencies
- Web3 Libraries
Web3Auth Dependencies
@web3auth/no-modal
This is the main Core package that contains the Web3Auth SDK.
npm install --save @web3auth/no-modal
@web3auth/auth-adapter
For using Custom Authentication, we need to use the Auth Adapter, where we can initialize the authentication details.
npm install --save @web3auth/auth-adapter
@web3auth/ethereum-provider
For using the EVM-compatible blockchains, we need to use the Ethereum Provider package, which will be used to connect to the blockchain.
npm install --save @web3auth/ethereum-provider
@web3auth/base
Since we're using typescript, we need the @web3auth/base
package to provide the types of the
different variables we'll be using throughout the app-building process. This reduces errors to a
very large extent.
npm install --save @web3auth/base
Web3 Libraries
web3
/ ethers
/ @solana/web3.js
According to your preference, you can choose to install the web3
or ethers
libraries, to talk to
the EVM compatible blockchains under the hood. For Solana, @solana/web3.js
is the recommended
choice.
We'll be using web3
for this guide.
npm install --save web3
Initialization
Once installed, your Web3Auth application needs to be initialized. Initialization is a 4 step process where we add all the config details for Web3Auth:
- Instantiation
- Configuration of Adapters
- Configuration of Plugins
- Initialization of the Web3Auth
Please make sure all of this is happening in your application constructor. This makes sure that Web3Auth is initialized when your application starts up.
For this guide, we're only focusing on the Instantiation, Configuration for the Auth Adapter (the default adapter that enables social logins) and Initialization of the Web3Auth SDK. To know more about the other things you can do with Web3Auth, check out our SDK Reference.
Instantiating Web3Auth
Importing the packages
import { WALLET_ADAPTERS, CHAIN_NAMESPACES, IProvider } from "@web3auth/base";
import { Web3AuthNoModal } from "@web3auth/no-modal";
import { AuthAdapter } from "@web3auth/auth-adapter";
import { EthereumPrivateKeyProvider } from "@web3auth/ethereum-provider";
Alongside the Web3Auth
and AuthAdapter
you need the above-mentioned packages from
@web3auth/base
for different initializations mentioned further in this guide.
Instantiate the Web3Auth SDK
import { Web3AuthNoModal } from "@web3auth/no-modal";
import { CHAIN_NAMESPACES } from "@web3auth/base";
const chainConfig = {
chainNamespace: CHAIN_NAMESPACES.EIP155,
chainId: "0x1",
rpcTarget: "https://rpc.ankr.com/eth",
displayName: "Ethereum Mainnet",
blockExplorerUrl: "https://etherscan.io",
ticker: "ETH",
tickerName: "Ethereum",
};
const web3auth = new Web3AuthNoModal({
clientId:
"BPi5PB_UiIZ-cPz1GtV5i1I2iOSOHuimiXBI0e-Oe_u6X3oVAbCiAZOTEBtTXw4tsluTITPqA8zMsfxIKMjiqNQ",
web3AuthNetwork: "sapphire_mainnet",
chainConfig,
});
Here, we're using the chainConfig
property to set the chainId and chainNamespace. The chainId
and chainNamespace
are the id and the namespace respectively of the chain you're connecting to.
We've initialized them for EVM for this guide. You can find the list of available providers
here to select from.
Additionally, sometimes you might face clogging in the network because the test network is a bit
clogged at that point. To avoid this, we can use the property rpcTarget
and pass over the URL of
the node you want to connect to.
Initializing the Auth Adapter
const privateKeyProvider = new EthereumPrivateKeyProvider({
config: { chainConfig },
});
const authAdapter = new AuthAdapter({
adapterSettings: {
clientId: "YOUR-WEB3AUTH-CLIENT-ID", //Optional - Provide only if you haven't provided it in the Web3Auth Instantiation Code
uxMode: "popup",
loginConfig: {
jwt: {
name: "Name of your choice",
verifier: "YOUR-VERIFIER-NAME-ON-WEB3AUTH-DASHBOARD",
typeOfLogin: "jwt",
clientId: "YOUR-CLIENT-ID-FROM-SOCIAL-PROVIDER",
},
},
},
privateKeyProvider,
});
web3auth.configureAdapter(authAdapter);
Here, you need to pass over your Web3Auth clientId
in the adapterSettings object and your Custom
Auth verifierName
and AWS Cognito clientId
in the loginConfig object. This makes sure that the
Auth Adapter can connect to the correct verifier and AWS Cognito server.
Initializing the Web3Auth SDK
await web3auth.init();
Initializing on Mobile Platforms
- For Mobile, depending on the platform, there will be different steps to initialize the SDK. Refer to the respective Mobile SDKs,
Authentication
Logging in
Once initialized, you can use the connectTo()
function to authenticate the user when they click
the login button.
import { WALLET_ADAPTERS } from "@web3auth/base";
await web3auth.connectTo(WALLET_ADAPTERS.AUTH, {
loginProvider: "jwt",
extraLoginOptions: {
domain: "https://YOUR-AWS-COGNITO-DOMAIN",
verifierIdField: "email",
response_type: "token",
scope: "email profile openid",
},
});
When connecting, your connectTo
function takes the arguments for the adapter you want to connect
to and the options for the login. The major thing to note here is the domain
option in the
extraLoginOptions
object. This is the domain of your AWS Cognito pool so that you can be
redirected to log in there directly from the Web3Auth Plug and Play No Modal SDK.
Get the User Profile
const user = await web3auth.getUserInfo();
console.log("User info", user);
Using the getUserInfo
function, you can get the details of the logged-in user. Please note that
these details are not stored anywhere in Web3Auth network, but are fetched from the id_token you
received from AWS Cognito and live in the frontend context.
Logout
await web3auth.logout();
Logging out your user is as simple as calling the logout
function.
Note: Currently, Web3Auth Plug and Play SDKs don't log out a user from AWS Cognito, so you can try calling this below endpoint to log out a user.
window.open("
YOUR-COGNITO-DOMAIN/logout?
client_id=YOUR-CLIENTID-FROM-AWS-COGNITO-DASHBOARD&
logout_uri=LOGOUT_URI&
redirect_uri=REDIRECT_URI
");
Interacting with Blockchain
So if you have completed this far, it means that you have successfully authenticated your user. Now,
you can use the provider returned by Web3Auth as web3auth.provider
to interact with your
blockchain. You can use the Provider SDKs to perform RPC Calls to your
blockchain.
Web3Auth is chain agnostic, ie. depending on whatever blockchain or layer-2 you use, Web3Auth can
easily support that. Web3Auth has native providers for EVM and Solana blockchains and for others,
you can get the private key in the user scope and make RPC calls. For standardising the type of
provider, Web3Auth Base provides a IProvider
from which you can create your own provider.
- Ethereum Provider gives you the capability of making RPC calls to the EVM compatible blockchains.
- Solana Provider gives you the capability of making RPC calls to the Solana blockchain.
- XRPL Provider gives you the capability of making RPC calls to the XRPL blockchain.
- If you want to use any other chain except Solana or EVM chains, for ex: Starknet, you can specify
the value of
chainNamespace
field as other in the Web3Auth SDK Constructor. Refer to: Using other blockchains
Get User Accounts
- Web3
- Ethers.js
const getAccounts = async () => {
if (!provider) {
console.log("provider not initialized yet");
return;
}
const web3 = new Web3(provider as any);
const userAccounts = await web3.eth.getAccounts();
console.log(userAccounts);
};
const getAccounts = async () => {
if (!provider) {
console.log("provider not initialized yet");
return;
}
// For ethers v5
// const ethersProvider = new ethers.providers.Web3Provider(this.provider);
const ethersProvider = new ethers.BrowserProvider(this.provider);
// For ethers v5
// const signer = ethersProvider.getSigner();
const signer = await ethersProvider.getSigner();
// Get the user's Ethereum public address
const address = signer.getAddress();
console.log(address);
};
View User Balance
- Web3
- Ethers.js
const getBalance = async () => {
if (!provider) {
console.log("provider not initialized yet");
return;
}
const web3 = new Web3(provider as any);
const accounts = await web3.eth.getAccounts();
const balance = await web3.eth.getBalance(accounts[0]);
console.log(web3.utils.fromWei(balance));
};
const getBalance = async () => {
if (!provider) {
console.log("provider not initialized yet");
return;
}
// For ethers v5
// const ethersProvider = new ethers.providers.Web3Provider(this.provider);
const ethersProvider = new ethers.BrowserProvider(this.provider);
// For ethers v5
// const signer = ethersProvider.getSigner();
const signer = await ethersProvider.getSigner();
// Get the user's Ethereum public address
const address = signer.getAddress();
// Get the user's balance in ether
// For ethers v5
// const balance = ethers.utils.formatEther(
// await ethersProvider.getBalance(address) // Balance is in wei
// );
const balance = ethers.formatEther(
await ethersProvider.getBalance(address), // Balance is in wei
);
console.log(balance);
};
Sign Message
- Web3
- Ethers.js
const signMessage = async () => {
if (!provider) {
console.log("provider not initialized yet");
return;
}
const web3 = new Web3(provider as any);
// Get user's Ethereum public address
const account = (await web3.eth.getAccounts())[0];
// Message
const message = "Hello MPC, Bye Bye SeedPhrase";
const typedMessage = [
{
type: "string",
name: "message",
value: message,
},
];
const params = [JSON.stringify(typedMessage), account];
const method = "eth_signTypedData";
const signedMessage = await this.provider.request({
method,
params,
});
console.log(signedMessage);
};
const signMessage = async () => {
if (!provider) {
console.log("provider not initialized yet");
return;
}
// For ethers v5
// const ethersProvider = new ethers.providers.Web3Provider(this.provider);
const ethersProvider = new ethers.BrowserProvider(this.provider);
// For ethers v5
// const signer = ethersProvider.getSigner();
const signer = await ethersProvider.getSigner();
const originalMessage = "YOUR_MESSAGE";
// Sign the message
const signedMessage = await signer.signMessage(originalMessage);
console.log(signedMessage);
};
Sign Transaction
const signTransaction = async () => {
if (!provider) {
console.log("provider not initialized yet");
return;
}
const web3 = new Web3(provider as any);
const accounts = await web3.eth.getAccounts();
const txRes = await web3.eth.signTransaction({
from: accounts[0],
to: accounts[0],
value: web3.utils.toWei("0.0001"),
chainId: 1, // change it to your specific chain id.
});
console.log(txRes.transactionHash);
};
Send Transaction
- Web3
- Ethers.js
const sendTransaction = async () => {
if (!provider) {
console.log("provider not initialized yet");
return;
}
const web3 = new Web3(provider as any);
const accounts = await web3.eth.getAccounts();
const txRes = await web3.eth.sendTransaction({
from: accounts[0],
to: accounts[0],
value: web3.utils.toWei("0.0001"),
chainId: 1, // change it to your specific chain id.
});
console.log(txRes.transactionHash);
};
const sendTransaction = async () => {
if (!provider) {
console.log("provider not initialized yet");
return;
}
// For ethers v5
// const ethersProvider = new ethers.providers.Web3Provider(this.provider);
const ethersProvider = new ethers.BrowserProvider(this.provider);
// For ethers v5
// const signer = ethersProvider.getSigner();
const signer = await ethersProvider.getSigner();
const destination = "0x40e1c367Eca34250cAF1bc8330E9EddfD403fC56";
// Convert 1 ether to wei
// For ethers v5
// const amount = ethers.utils.parseEther("0.001");
const amount = ethers.parseEther("0.001");
// Submit transaction to the blockchain
const tx = await signer.sendTransaction({
to: destination,
value: amount,
maxPriorityFeePerGas: "5000000000", // Max priority fee per gas
maxFeePerGas: "6000000000000", // Max fee per gas
});
// Wait for the transaction to be mined
const receipt = await tx.wait();
console.log(receipt.transactionHash);
};
Additional Reading: Setup Custom Authentication using AWS Cognito Authorization code flow
Difference between Implicit and Authorization code flow
In the Implicit flow, the frontend gets the JWT id_token
from AWS Cognito directly. This flow
is leveraged by the Web3Auth SDK, which handles all the callbacks by itself and the user just needs
to set the AuthAdapter
and connect to the Web3Auth SDK, adding the AWS Cognito client_id
in it.
Hence, this is the implicit flow and is the most suitable flow for setting up your authentication
via AWS Cognito and Web3Auth.
In the Authorization code flow, you have to spin up a server on your own where you'll receive
the authorisation code
from AWS Cognito. For Web3Auth, still requires an id_token
to be sent to
the AuthAdapter to log the user in the frontend context. This is where you need to make another call
from the backend server to AWS Cognito to get the id_token
and pass it over to your frontend where
you can send it to the Web3Auth SDK.
Setting up your application with Authorization code Flow
- In addition to the steps above, select
Authorization code grant
from Allowed OAuth Flows on your AWS Cognito Console's App client settings page and add callback URLs for both your backend and frontend - On the frontend, your login button should hit on the following URL
https://{YOUR_DOMAIN}.auth.{region}.amazoncognito.com/oauth2/authorize?
response_type=code&
client_id={CLIENT_ID_FROM_AWS}&
redirect_uri=https://{YOUR_APP}/redirect_uri&
state=STATE&
scope=openid+profile+aws.cognito.signin.user.admin
- This is where the user is redirected to a login screen by AWS Cognito.
- On successful login, the user will be redirected to your backend server with authorization
code
as a query parameter. - In the backend server, you can use the
code
to get theid_token
from AWS Cognito and redirect the user to the frontend withid_token
as a query parameter
app.get('/callback', (req, res) => {
var options = {
method: 'POST',
url: 'https://{YOUR_DOMAIN}.auth.{REGION}.amazoncognito.com/oauth2/token',
headers: {
'content-type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic ' + Buffer.from(CLIENT_ID_FROM_AWS + ':' + CLIENT_SECRET_FROM_AWS).toString('base64')
},
form:{
grant_type: 'authorization_code',
client_id: 'CLIENT_ID_FROM_AWS',
code: req.query.code, // the authorisation code you got from AWS Cognito
redirect_uri: 'https://{YOUR_FRONTEND_URL}',
}
};
var id_token = "";
request(options, function (error, response, data) {
id_token = JSON.parse(data)["id_token"];
redirect_url = "http://{YOUR_FRONTEND_URL}?token=%22+id_token;
// Redirection URL to frontend with `id_token` as a query parameter
res.redirect(redirect_url);
});
})
- In the frontend, pass on this
id_token
to the Web3Auth SDK to authenticate the user
await web3auth.connectTo(WALLET_ADAPTERS.AUTH, {
loginProvider: "jwt",
extraLoginOptions: {
id_token: "YOUR_ID_TOKEN_FETCHED_FROM_BACKEND_SERVER",
domain: "https://YOUR-AWS-COGNITO-DOMAIN",
verifierIdField: "sub",
},
});
Example code
The code for the application we developed in this guide can be found in the Web3Auth Cognito Example. Check it out and try running it locally yourself!
Questions?
Ask us on Web3Auth's Community Support Portal