How to use Amazon Cognito without Amplify - amazon-web-services

I'm just now diving into Cognito. The AWS setup has been fairly straight-forward, easy.
We have a variety of apps, webapps, and services and we'd like those to make use of the Cognito service. I've experience setting up similar with Auth0, but because we've been leveraging a number of Amazon Web Services, it really makes sense to use Cognito as well.
Everywhere I look, every guide eventually references Amplify client-side library and cli. We have existing apps and services, and really don't want to change tooling or import anything unnecessary to add bloat and complexity. Is there a way to use Cognito service without Amplify libraries? Is there a lightweight Cognito-only client library for interfacing with the Cognito service, authentication-and-authorization flow?

Update 03 Dec 2021
After re:Invent 2021, "Amplify Admin UI" was renamed to "Amplify Studio". With extra powers now:
automatically translates designs made in Figma to human-readable React UI component code
https://aws.amazon.com/blogs/mobile/aws-amplify-studio-figma-to-fullstack-react-app-with-minimal-programming/
===============
Original Answer
To start, I want to clarify that "Amplify" is an umbrella term for multiple things. We have:
Amplify Libraries (UI/JS)
Amplify CLI (to create cloud-native applications)
Amplify Console (ci/cd and hosting for full-stack web apps)
Amplify Admin UI (UI to create and configure full-stack web apps)
You can check the homepage for more clarification - https://docs.amplify.aws/
Is there a lightweight Cognito-only client library for interfacing with the Cognito service, authentication-and-authorization flow?
Behind the scenes, Amplify uses amazon-cognito-identity-js library to interface with Amazon Cognito. You can install that directly via npm install amazon-cognito-identity-js.
The source code has been moved to the Amplify Libraries (e.g. amplify-js) repository. Once again, is part of the "Amplify" umbrella under the first category "Amplify Libraries".
Is there a way to use Cognito service without Amplify libraries?
Another approach that you can do, is to use Amazon Cognito as an OAuth server. When you create an Amazon Cognito Hosted UI Domain, it provides you an OAuth 2.0 compliant authorization server.
You can create your own API/Backend for Signup/Login endpoints and exchange tokens/credentials with the Amazon Cognito OAuth server without using aws-sdk or any 3rd party dependency library.
I wrote a walkthrough example, how to configure your User Pool, endpoints that you need to talk to using Node.js, you can find it here: https://github.com/oieduardorabelo/node-amazon-cognito-oauth
You can follow the same idea for any other language.

As mentioned by #oieduardorabelo, you can simply install 'amazon-cognito-identity-js' where you can also find well done examples on npm.
Here is my test code to easily understand this lib. You must have already built the infrastructure on AWS (userPool, userClient and add a new user to test sign in - in my case the user has to change the password on first login so I added this use case on my script):
import * as AmazonCognitoIdentity from 'amazon-cognito-identity-js';
var authenticationData = {
Username: 'email',
Password: 'password',
};
var authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);
var poolData = {
UserPoolId: 'us-east-1_userpoolid',
ClientId: '26pjexamplejpkvt'
};
var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
var cognitoUser = userPool.getCurrentUser();
console.log(cognitoUser);
if (!cognitoUser) {
var userData = {
Username: authenticationData.Username,
Pool: userPool
};
var cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
var accessToken = result.getAccessToken().getJwtToken();
var idToken = result.idToken.jwtToken;
console.log('Success', accessToken, idToken);
},
newPasswordRequired: function (userAttributes, requiredAttributes) {
delete userAttributes.email_verified;
cognitoUser.completeNewPasswordChallenge('DemoPassword1!', userAttributes, {
onSuccess: (data) => {
console.log(data);
},
onFailure: function (err) {
alert(err);
}
});
},
onFailure: function (err) {
alert(err);
},
});
}
If someone is interested in setup this test project from scratch run:
npm init -y
npm i -D webpack webpack-cli
npm i amazon-cognito-identity-js
in webpack.config.js:
var path = require('path');
module.exports = {
entry: './src/app.js',
mode: 'development',
output: {
path: path.resolve(__dirname, "dist"),
filename: 'main.js',
}
}
Create a new file in ./src/app.js where add the previous amazonCognitoIdentity code with the right AWS info ref and create ./dist/index.html whith:
...
<body>
<script src="main.js"></script>
</body>
in package.json add script "watch":
...
"scripts": {
"watch": "webpack --watch",
}
Finally run it:
npm run watch
and open the index.html directly on the browser with dev console as well.
Hopefully useful for someone.

As a result of research on the topic of using Amazon Cognito without Amplify in React, I came across such a sandbox. Switching from router 5 to router 6 probably won't be a problem. The main gold here is this hook. The rest of the implementation can be found in the sandbox: https://codesandbox.io/s/cognito-forked-f02htu
const Pool_Data = {
UserPoolId: "xxx",
ClientId: "yyy"
};
export default function useHandler() {
const [state, setstate] = useState({
loading: false,
isAuthenticated: false
});
const { loading, isAuthenticated } = state;
const userPool = new CognitoUserPool(Pool_Data);
const getAuthenticatedUser = useCallback(() => {
return userPool.getCurrentUser();
}, []);
console.log(getAuthenticatedUser());
useEffect(() => {
getAuthenticatedUser();
}, [getAuthenticatedUser]);
const signOut = () => {
return userPool.getCurrentUser()?.signOut();
};
console.log("I am here", getAuthenticatedUser()?.getUsername());
return {
loading,
isAuthenticated,
userPool,
getAuthenticatedUser,
signOut
};
}

I wrote an article a couple of years ago explaining how to do this.
The article talks about Amplify but as was mentioned in another response, that's more of an umbrella term, in the article we are using mostly UI components provided by the Amplify project.
you can find it here: https://medium.com/#mim3dot/aws-amplify-cognito-part-2-ui-components-935876fabad3

Related

AWS Amplify post request fails with "status code 403 at node_modules/axios"

I configured and initialized AWS Amplify for my ReactNative/Expo app and added a REST Api. Im new to AWS in general, but im assuming that once I add the API, my project is populated with amplify/backend folders and files and is ready for consumption.
So i tried to create a simple post request to create an item in my DynamoDB table with
import { Amplify, API } from "aws-amplify";
import awsconfig from "./src/aws-exports";
Amplify.configure(awsconfig);
const enterData = async () => {
API.post("API", "/", {
body: {
dateID: "testing",
},
headers: {
Authorization: `Bearer ${(await Auth.currentSession())
.getIdToken()
.getJwtToken()}`
}
})
.then((result) => {
// console.log(JSON.parse(result));
})
.catch((err) => {
console.log(err);
});
};
const signIn = async () => {
Auth.signIn('test#test.com', 'testpassword')
.then((data) => {
console.log(data)
enterData() //enterData is attempted after signin is confirmed.
})
.catch((err) => {
console.log(err)
})
}
signIn()
I did not touch anything else in my project folder besides including the above in my App.tsx because im unsure if i need to and where. I got a 403 error code and it "points" to the axios package but im not sure if issue is related to aws integration.
I configured the REST Api with restricted access where Authenticated users are allowed to CRUD, and guests are allowed to Read. How could I even check if I am considered an "Authorized User" .
Yes, AWS Amplify API category uses Axios under the hood so axios is related to your problem.
Probably you get 403 because you didn't authorized, for Rest API's you need to set authorization headers,
I don't know how is your config but you can take help from this page. Please review the "Define Authorization Rules" section under the API(REST) section.
https://docs.amplify.aws/lib/restapi/authz/q/platform/js/#customizing-http-request-headers
To check authorization methods, you can use "Auth" class like that also you can see auth class usage in the above link.
import { Amplify, API, Auth } from "aws-amplify";
https://aws-amplify.github.io/amplify-js/api/classes/authclass.html

AWS Amplify Multiple frontends single API & Backend

I have 2 amplify webapps and I want that both are using the same AWS backend. So i followed the instructions on https://docs.amplify.aws/cli/teams/multi-frontend/#workflow. App A has the full amplify backend src and App B shall use these too. So I run on App B.
amplify pull
>Amplify AppID found: df4xxxxxxx. Amplify App name is: XXXXX
>Backend environment dev found in Amplify Console app: XXXXX
Seems to work.
But when I now try to make an api call via:
AWS_API = 'api_name_on_aws';
async getUserInfosByUsername(username) {
var userInfos;
await API.get(this.AWS_API, `/users/infos/testuser`,
{
headers: {},
response: true,
body: {},
queryStringParameters: {},
})
.then((response) => {
userInfos = response;
})
.catch((error) => {
console.log(error.response);
});
return userInfos;
}
then no api request will send. (I can see within the google chrome dev console/network that no request is send).
The "request" method is just return "undefined" and thats all... On App A everything is working fine.
Did I miss something? Should I do something else that App B can use the API of APP A?

NextJs website + Cognito auth + next-auth npm package + S3 -> Getting CLIENT_FETCH_ERROR

I build my front-end using NextJs and am hosting the website on AWS S3. Everything was fine until I tried to add cognito authentication. I found this npm package that was supposed to make everything easy: next-auth.
However I keep getting errors:
When loading the page, there is console error: "CLIENT_FETCH_ERROR". Next auth says I need to declare an env variable called NEXTAUTH_URL (https://next-auth.js.org/errors) which I have done in my .env file.
When trying to click login it redirects me to https://example.com/api/auth/error with a 403 in the public website. On localhost, it actually brings me to a page (http://localhost:3000/api/auth/signin?callbackUrl=http%3A%2F%2Flocalhost%3A3000%2F) with 2 buttons (one to sign in with Cognito and the other with Google).
Do you think this is because S3 doesn't actually have environment variables so NEXTAUTH_URL is undefined? Should I move to Vercel or another AWS service or is there something else that could be wrong?
My pages/api/auth/[...nextauth].js file:
import NextAuth from 'next-auth/next';
//import Providers from 'next-auth/providers';
import GoogleProvider from 'next-auth/providers/google';
import CognitoProvider from 'next-auth/providers/cognito';
export default NextAuth({
providers: [
CognitoProvider({
clientId: process.env.NEXT_PUBLIC_COGNITO_CLIENT_ID,
clientSecret: process.env.COGNITO_CLIENT_SECRET,
issuer: process.env.COGNITO_ISSUER,
domain: process.env.COGNITO_DOMAIN,
}),
],
theme: {
colorScheme: 'light',
},
debug: process.env.NODE_ENV === 'development' ? true : false,
secret: process.env.NEXTAUTH_SECRET,
});

How to use different URLs depending of Nuxt's environment?

In vuex store I call up the API Gateway api like this:
import apigClientFactory from 'aws-api-gateway-client';
const apigClient = apigClientFactory.newClient({
invokeUrl: 'https://blahblah.us-east-2.amazonaws.com/dev'
})
I'd like to invoke dev or prod versions of the API dependent on whether the app is in development or production mode. How can I achieve this?
I'm assuming that this is the proper way to go about having a web app use API Gateway. When I am doing development work, it should invoke a dev stage of the API that in turn uses development stage tools (like $latest for lambda). Once those tools are ready, they can be pushed to the stage that the production API is using.
UPDATE: I am not proud of this but I was able to just use an if/else statement to define the correct url based on stage like so:
if (process.env.NODE_ENV !== 'production') {
var apigClient = apigClientFactory.newClient({
invokeUrl: 'apiurl/dev'
});
} else {
var apigClient = apigClientFactory.newClient({
invokeUrl: 'apiurl/prod'
});
}
You should use env variables and inject the proper dev or production URL there. This way, you could achieve a safe, quick and easy separation.
Give a look to my answer here to see the whole configuration needed: https://stackoverflow.com/a/67705541/8816585
export default ({ $config: { myFancyAwsVariableDefinedInNuxtConfig } }) => {
const apigClient = apigClientFactory.newClient({
invokeUrl: myFancyAwsVariableDefinedInNuxtConfig
})
}

AWS Amplify - Disable Current User Account

AWS Amplify Authentication module has some methods for actions like sign in, sign up, forgot password etc. Even one can let the user to update his/her info through like:
import { Auth } from 'aws-amplify'
// Auth API Sign-in sample
Auth.signIn(username, password)
.then(user => console.log(user))
.catch(err => console.log(err))
// Auth API Change info sample
let result = await Auth.updateUserAttributes(user, {
'email': 'me#anotherdomain.com',
'family_name': 'Lastname'
})
However, I could not see anyway to disable (beware, not to delete) an account.
So, a user can sign up to a web application, but cannot deactivate it using AWS Amplify? If not, are there any other ways for disabling an AWS Cognito User Pool user via Javascript code?
I went through the AWS Documentation for the Cognito User Pools API and found some methods that let a function with admin privileges disable(not delete) a Cognito User Pool account!
Here is the link to the documentation on the AWS website:
https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminDisableUser.html
There is also a method to reenable the user:
https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminEnableUser.html
The javascript implementation for this can be found here:
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityServiceProvider.html#adminDisableUser-property
The code would be something like this using the AWS SDK for JS:
var cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider({apiVersion: '2016-04-18'});
var params = {
UserPoolId: 'STRING_VALUE', /* required */
Username: 'STRING_VALUE' /* required */
};
cognitoidentityserviceprovider.adminDisableUser(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});