AWS Amplify Configuration: Re-Use Authentication Resource - javascript

I've been trying to configure my Application with an existing Backend as suggested in this link, from the amplifyConfig.ts configuration file (see below)
Then, I import the configuration file in the Login.tsx page and invoke it by passing Amplify.configure(currentConfig)
The first unexpected behavior is that, regardless I define authenticationFlowType: 'CUSTOM_AUTH', when I call the Auth.Signup method, the authenticationFlowType is still defined as "USR_SRP_AUTH".
Second, whenever I change the AmplifyConfig.ts file, I have to clear all the browsing data in order for those changes to work.
This behavior suggests me I'm doing something wrong, I understand I can deal this via cli, however I would prefer to handle this via code.
Thanks a lot!!
amplifyConfig.ts:
import Amplify, { Auth } from 'aws-amplify';
Amplify.configure({
Auth: {
// REQUIRED - Amazon Cognito Region
region: 'XX-XXXX-X',
// OPTIONAL - Amazon Cognito User Pool ID
userPoolId: 'XX-XXXX-X_abcd1234',
// OPTIONAL - Manually set the authentication flow type. Default is 'USER_SRP_AUTH'
authenticationFlowType: 'CUSTOM_AUTH',
}
}
});
// You can get the current config object
const currentConfig = Auth.configure();
Login.tsx:
import Amplify, { Auth } from 'aws-amplify';
import currentConfig from '../../services/amplifyConfig';
export function LoginMenu() {
Amplify.configure(currentConfig);
// redundant configuration of authenticationFlowType
Auth.configure({
authenticationFlowType: 'CUSTOM_AUTH',
});
async function onSignup(event: FormEvent) {
event?.preventDefault();
try {
const { user } = await Auth.signUp(UserData);
console.log(user);
} catch (error) {
console.log('error signing up:', error);
}
}
}

Related

Is there a way to use firebase secret environment variables in slack/bolt

We operate bots by combining Firebase and Slack/bolt.
I currently use functions.config()to manage my Slack tokens and secrets, but would like to migrate to using Secret Manager.
https://firebase.google.com/docs/functions/config-env#secret-manager
boltapp.js
const functions = require("firebase-functions");
const { App, ExpressReceiver, subtype } = require("#slack/bolt");
const config = functions.config();
const expressReceiver = new ExpressReceiver({
signingSecret: process.env.SLACK_SECRET,
endpoints: "/events",
processBeforeResponse: true,
});
const app = new App({
receiver: expressReceiver,
token: process.env.SLACK_TOKEN
});
app.error(error => { console.error(error) });
app.use(async ({ client, payload, context, next }) => {
console.info('It\'s payload', JSON.stringify(payload))
if (!context?.retryNum) {
await next();
} else {
console.debug('app.use.context', context);
}
});
//**bot processing**//
// https://{your domain}.cloudfunctions.net/slack/events
module.exports = functions
.runWith({ secrets: ["SLACK_TOKEN","SLACK_SECRET"] })
.https.onRequest(expressReceiver.app);
But when I rewrote it for migration, I got the following error.
Is there a way to rewrite the code while avoiding this error?
Failed to load function definition from source: FirebaseError: Failed to load function definition from source: Failed to generate manifest from function source: Error: Apps used in a single workspace can be initialized with a token. Apps used in many workspaces should be initialized with oauth installer options or authorize.
Since you have not provided a token or authorize, you might be missing one or more required oauth installer options. See https://slack.dev/bolt-js/concepts#authenticating-oauth for these required fields.

Firebase: Re-authenticate the current user inside a Cloud Function

I am implementing a cloud function for updating the current user's password.
Basically, the logic I want to follow is:
(Client side)
0. Complete form and submit the data (current password and new password).
(Backend)
1. Get the current user email from the callable function context.
2. Re-authenticate the current user using the provided current password.
2.1. If success, change the password and send a notification email.
2.2. Else, throw an error.
Here is my current code:
const { auth, functions } = require("../../services/firebase");
...
exports.updatePassword = functions
.region("us-central1")
.runWith({ memory: "1GB", timeoutSeconds: 120 })
.https.onCall(async (data, context) => {
const { currentPassowrd, newPassword } = data;
const { email, uid: userId } = context.auth.token;
if (!userId) {
// throw ...
}
try {
//
// Problem: `firebase-admin` authentication doesn't include
// the `signInWithEmailAndPassword()` method...
//
await auth.signInWithEmailAndPassword(email, currentPassowrd);
await auth.updateUser(userId, {
password: newPassword,
});
sendPasswordUpdateEmail(email);
} catch (err) {
// ...
throw AuthErrors.cannotUpdatePassword();
}
});
My problem is that the firebase-admin package doesn't include the signInWithEmailAndPassword, and I need a way to handle this, to check that "currentPassword" is correct, inside my function.
My other option, if the one I have described is not possible, is to update the password using the firebase sdk in the client side, and then to call a firebase function to send the notification email.
Strictly speaking you don't need to re-authenticate the user in the Cloud Function: If you get a value for context.auth.uid in your Callable Cloud Function, it means that the user is authenticated in the front-end and you can therefore safely call the updateUser() method.
If you want to deal with the case when the user left his device opened, and someone updates his password, as explained in the comments under your question, I would suggest you use the reauthenticateWithCredential() method in the front-end, which re-authenticates a user using a fresh credential.
Do as follows:
import {
EmailAuthProvider,
getAuth,
reauthenticateWithCredential,
} from 'firebase/auth'
const email = auth.currentUser.email;
// Capture the password value
// e.g. via a pop-up window
const password = ...;
const auth = getAuth();
const credential = EmailAuthProvider.credential(
email,
password
);
await reauthenticateWithCredential(
auth.currentUser,
credential
);
// If no error is thrown, you can call the Callable Cloud Function, knowing the user has just re-signed-in.

Unable to integrate Stripe checkout into Vue JS app

I am trying to redirect to stripe checkout using a click event that calls the payStripe function
<button #click="payStripe" class="btn btn-primary" type="button">Place Order</button>
I have imported stripe into my Vue component like so;
import { loadStripe } from "#stripe/stripe-js";
const stripe = loadStripe('MY-KEY');
I am using Firebase cloud functions and axois to fetch the session and store this to a data property, this works fine.
But, the payStripe method, when called, gives the following error;
Error in v-on handler: "TypeError: stripe.redirectToCheckout is not a function"
Here is the function i am using which, from all accounts is similar to the Stripe API docs;
data() {
return {
sessionId: null,
}
},
methods: {
//This function sends us to the stripe checkout
payStripe() {
stripe.redirectToCheckout({
sessionId: this.sessionId
})
.then(function(result) {
console.log(result);
}).catch(function(error) {
console.log(error);
});
}
},
I had original issues with babel-core, so i updated to #babel/core to get rid of rest operator issues when compling code, but faced with this new issue. Any advice would be great. Thank you
According to the documentation here, loadStripe returns a promise that you must wait to resolve.
Try something like
import { loadStripe } from "#stripe/stripe-js"
const stripeInit = loadStripe('MY-KEY') // returns a promise
and when you want to use stripe
methods: {
//This function sends us to the stripe checkout
payStripe() {
// wait for the promise to resolve first
stripeInit.then(stripe => {
stripe.redirectToCheckout({
sessionId: this.sessionId
}).then(function(result) {
console.log(result);
}).catch(function(error) {
console.error(error);
});
})
}
}
Using Laravel Homestead as a vm, I turned ssl: true in Homestead.yaml, and entered https://site.test manually in the URL. I didn't get a valid https certificate, but the vue-stripe components load and it works

nextjs route middleware for authentication

I'm trying to figure out an appropriate way of doing authentication, which I know is a touchy subject on the GitHub issue page.
My authentication is simple. I store a JWT token in the session. I send it to a different server for approval. If I get back true, we keep going, if I get back false, it clears the session and puts sends them to the main page.
In my server.js file I have the following (note- I am using the example from nextjs learn and just adding isAuthenticated):
function isAuthenticated(req, res, next) {
//checks go here
//if (req.user.authenticated)
// return next();
// IF A USER ISN'T LOGGED IN, THEN REDIRECT THEM SOMEWHERE
res.redirect('/');
}
server.get('/p/:id', isAuthenticated, (req, res) => {
const actualPage = '/post'
const queryParams = { id: req.params.id }
app.render(req, res, actualPage, queryParams)
})
This works as designed. If I refresh the page /p/123, it will redirect to the /. However, if I go there via a next/link href, it doesn't. Which I believe is because it's not using express at this point but next's custom routing.
Is there a way I can bake in a check for every single next/link that doesn't go through express so that I can make sure the user is logged in?
Tim from the next chat helped me solve this. Solution can be found here but I will quote him so you all can see:
You can do the check in _app.js getInitialProps and redirect like this
Example of how to use it
_app.js documentation
I've also created an example skeleton template you can take a look at.
--
EDIT July 2021 - WARNING: This is an outdated solution and has not been confirmed to work with the latest versions of next.js. Use skeleton template at your own risk.
Edit: Updated answer for Next 12.2+
Note: The below contents is copied from the official blog post since SO generally discourages links that can become stale/dead over time
https://nextjs.org/blog/next-12-2#middleware-stable
Middleware is now stable in 12.2 and has an improved API based on feedback from users.
// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
// If the incoming request has the "beta" cookie
// then we'll rewrite the request to /beta
export function middleware(req: NextRequest) {
const isInBeta = JSON.parse(req.cookies.get('beta') || 'false');
req.nextUrl.pathname = isInBeta ? '/beta' : '/';
return NextResponse.rewrite(req.nextUrl);
}
// Supports both a single value or an array of matches
export const config = {
matcher: '/',
};
Migration guide
https://nextjs.org/docs/messages/middleware-upgrade-guide
Breaking changes
No Nested Middleware
No Response Body
Cookies API Revamped
New User-Agent Helper
No More Page Match Data
Executing Middleware on Internal Next.js Requests
How to upgrade
You should declare one single Middleware file in your application, which should be located next to the pages directory and named without an _ prefix. Your Middleware file can still have either a .ts or .js extension.
Middleware will be invoked for every route in the app, and a custom matcher can be used to define matching filters. The following is an example for a Middleware that triggers for /about/* and /dashboard/:path*, the custom matcher is defined in an exported config object:
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
return NextResponse.rewrite(new URL('/about-2', request.url))
}
// Supports both a single string value or an array of matchers
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
}
Edit: Outdated answer for next > 12 and < 12.2
With the release of Next.js 12, there's now beta support for middleware using Vercel Edge Functions.
https://nextjs.org/blog/next-12#introducing-middleware
Middleware uses a strict runtime that supports standard Web APIs like fetch. > This works out of the box using next start, as well as on Edge platforms like Vercel, which use Edge Functions.
To use Middleware in Next.js, you can create a file pages/_middleware.js. In this example, we use the standard Web API Response (MDN):
// pages/_middleware.js
export function middleware(req, ev) {
return new Response('Hello, world!')
}
JWT Authentication example
https://github.com/vercel/examples/tree/main/edge-functions/jwt-authentication
in next.config.js:
const withTM = require('#vercel/edge-functions-ui/transpile')()
module.exports = withTM()
in pages/_middleware.js:
import { NextRequest, NextResponse } from 'next/server'
import { setUserCookie } from '#lib/auth'
export function middleware(req: NextRequest) {
// Add the user token to the response
return setUserCookie(req, NextResponse.next())
}
in pages/api/_middleware.js:
import type { NextRequest } from 'next/server'
import { nanoid } from 'nanoid'
import { verifyAuth } from '#lib/auth'
import { jsonResponse } from '#lib/utils'
export async function middleware(req: NextRequest) {
const url = req.nextUrl
if (url.searchParams.has('edge')) {
const resOrPayload = await verifyAuth(req)
return resOrPayload instanceof Response
? resOrPayload
: jsonResponse(200, { nanoid: nanoid(), jwtID: resOrPayload.jti })
}
}
in pages/api/index.js:
import type { NextApiRequest, NextApiResponse } from 'next'
import { verify, JwtPayload } from 'jsonwebtoken'
import { nanoid } from 'nanoid'
import { USER_TOKEN, JWT_SECRET_KEY } from '#lib/constants'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'GET') {
return res.status(405).json({
error: { message: 'Method not allowed' },
})
}
try {
const token = req.cookies[USER_TOKEN]
const payload = verify(token, JWT_SECRET_KEY) as JwtPayload
res.status(200).json({ nanoid: nanoid(), jwtID: payload.jti })
} catch (err) {
res.status(401).json({ error: { message: 'Your token has expired.' } })
}
}
There is no middleware for no API routes in NextJS, but there are HOCs, which you can use to connect to db - select the user, etc:
https://hoangvvo.com/blog/nextjs-middleware

How to change email in firebase auth?

I am trying to change/update a user's email address using :
firebase.auth().changeEmail({oldEmail, newEmail, password}, cb)
But I am getting ...changeEmail is not a function error. I found the reference here from the old firebase docu.
So how to I do it in the 3.x version? Because I cant find a reference in the new documentation.
You're looking for the updateEmail() method on the firebase.User object: https://firebase.google.com/docs/reference/js/firebase.User#updateEmail
Since this is on the user object, your user will already have to be signed in. Hence it only requires the password.
Simple usage:
firebase.auth()
.signInWithEmailAndPassword('you#domain.example', 'correcthorsebatterystaple')
.then(function(userCredential) {
userCredential.user.updateEmail('newyou#domain.example')
})
If someone is looking for updating a user's email via Firebase Admin, it's documented over here and can be performed with:
admin.auth().updateUser(uid, {
email: "modifiedUser#example.com"
});
FOR FIREBASE V9 (modular) USERS:
The accepted answer will not apply to you. Instead, you can do this, i.e., import { updateEmail } and use it like any other import. The following code was copy/pasted directly from the fb docs at https://firebase.google.com/docs/auth/web/manage-users
Happy coding!
import { getAuth, updateEmail } from "firebase/auth";
const auth = getAuth();
updateEmail(auth.currentUser, "user#example.com").then(() => {
// Email updated!
// ...
}).catch((error) => {
// An error occurred
// ...
});
You can do this directly with AngularFire2, you just need to add "currentUser" to your path.
this.af.auth.currentUser.updateEmail(email)
.then(() => {
...
});
You will also need to reauthenticate the login prior to calling this as Firebase requires a fresh authentication to perform certain account functions such as deleting the account, changing the email or the password.
For the project I just implemented this on, I just included the login as part of the change password/email forms and then called "signInWithEmailAndPassword" just prior to the "updateEmail" call.
To update the password just do the following:
this.af.auth.currentUser.updatePassword(password)
.then(() => {
...
});
updateEmail needs to happen right after sign in due to email being a security sensitive info
Example for Kotlin
// need to sign user in immediately before updating the email
auth.signInWithEmailAndPassword("currentEmail","currentPassword")
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// Sign in success now update email
auth.currentUser!!.updateEmail(newEmail)
.addOnCompleteListener{ task ->
if (task.isSuccessful) {
// email update completed
}else{
// email update failed
}
}
} else {
// sign in failed
}
}
async updateEmail() {
const auth = firebase.auth();
try {
const usercred = await auth.currentUser.updateEmail(this.email.value);
console.log('Email updated!!')
} catch(err) {
console.log(err)
}
}
You can use this to update email with Firebase.
Firebase v9:
const changeEmail = (userInput) => {
const { newEmail, pass } = userInput
signInWithEmailAndPassword(auth, oldEmail, pass)
.then(cred => updateEmail(cred.user, newEmail))
}

Categories