How to cutomise the url sent with Meteor Accounts.ui forgetpassword - meteor

I'm using accounts.ui for forget password
Accounts.forgotPassword({email: "test#test.com"}, function (e, r) {
if (e) {
console.log(e.reason);
} else {
// success
}
});
When I send the email for forget password I simply get below email
Can someone tell me how to simply change the url and send the token with the same token
I tried using below code but that didn't work
main.js(client)
Meteor.startup(() => {
Accounts.resetPassword.text = function(user, url) {
url = url.replace('#/', '/');
console.log("url ==== ", url)
return `Click this link to reset your password: ${url}`;
}
});

On the server side:
Accounts.emailTemplates.resetPassword.subject = function () {
return "Forgot your password?";
};
Accounts.emailTemplates.resetPassword.text = function (user, url) {
return "Click this link to reset your password:\n\n" + url;
};
Read: https://docs.meteor.com/api/passwords.html#Accounts-emailTemplates

To change the resetPassword url to a custom one you have to run below code on your server (inside of /server/main.js file).
Accounts.urls.resetPassword = function (token) {
return FlowRouter.url("/reset-password/:token/", { token });
};
In this case, I am using a FlowRouter to generate that URL but you could technically just return a template literal if you like.
If the above code is in the server main.js file and you run Accounts.forgotPassword() function on the frontend from a localhost, it would generate this link:
http://localhost:3000/reset-password/C9cGfgaLEgGXbCVYJcCLnDYiRi3XJpmt2irLOOaKe56

Related

How to redirect to starting point after authorizing with auth0 in a Nextjs application using #auth0/nextjs-auth0

I'm currently using auth0 to authenticate users in a Next.js application.
I'm using the #auth0/nextjs-auth0 SDK and following along with the documentation.
However, I'm having trouble figuring out how to redirect users dynamically after login based on the page they accessed the login form from.
In the app I’m currently trying to build, users can log in from “/” which is the home page, and from the navbar element in “/browse”. However, after logging in, it always redirects back to “/”, while I would like to redirect users to “/browse” or "/browse/[id] if that is where they began the login process from.
I’ve tried using https://community.auth0.com/t/redirecting-to-another-page-other-than-using-nextjs-auth0/66920 as a guide but this method only allows me to redirect to a pre-defined route. I would like to know how I could make the redirect URL dynamic.
Thanks in advance!
Edit: I’ve managed to find a solution for now by digging in to the req object and setting the returnTo value to “referer”.
import { handleAuth, handleLogin } from '#auth0/nextjs-auth0';
const getLoginState = (req, loginOptions) => {
return {
returnTo: req.headers.referer
};
};
export default handleAuth({
async login(req, res) {
try {
await handleLogin(req, res, { getLoginState });
} catch (err) {
res.status(err.status ?? 500).end(err.message)
}
}
});
I’m not seeing any obvious problems so far but I’m not entirely sure if this method has any drawbacks, so I would appreciate any feedback.
How about this?
Step 1: Initialize Auth0 SDK
https://auth0.github.io/nextjs-auth0/modules/instance.html#initauth0
# /lib/auth0,js
import { initAuth0 } from "#auth0/nextjs-auth0";
export default initAuth0({
secret: process.env.SESSION_COOKIE_SECRET,
issuerBaseURL: process.env.NEXT_PUBLIC_AUTH0_DOMAIN,
baseURL: process.env.NEXT_PUBLIC_BASE_URL,
clientID: process.env.NEXT_PUBLIC_AUTH0_CLIENT_ID,
clientSecret: process.env.AUTH0_CLIENT_SECRET,
routes: {
callback:
process.env.NEXT_PUBLIC_REDIRECT_URI ||
"http://localhost:3000/api/auth/callback",
postLogoutRedirect:
process.env.NEXT_PUBLIC_POST_LOGOUT_REDIRECT_URI ||
"http://localhost:3000",
},
authorizationParams: {
response_type: "code",
scope: process.env.NEXT_PUBLIC_AUTH0_SCOPE,
},
session: {
absoluteDuration: process.env.SESSION_COOKIE_LIFETIME,
},
});
Step 2: Configure Login
https://auth0.github.io/nextjs-auth0/modules/handlers_login.html#handlelogin
https://auth0.github.io/nextjs-auth0/interfaces/handlers_login.loginoptions.html#returnto
# /pages/api/auth/login.js
import auth0 from "../../../lib/auth0";
export default async function login(req, res) {
let options = {
returnTo: 'http://localhost:3000/dashboard'
}
try {
await auth0.handleLogin(req, res, options);
} catch (error) {
console.error(error);
res.status(error.status || 500).end(error.message);
}
}
Now you will land on the dashboard page after successfully authenticating.
Step 3: Helpful Sanity Check
create /pages/api/auth/callback.js with the following content
import auth0 from "../../../lib/auth0";
const afterCallback = (req, res, session, state) => {
// console.log(session)
console.log(state)
return session
};
export default async function callback(req, res) {
try {
console.log(auth0)
await auth0.handleCallback(req, res, { afterCallback });
} catch (error) {
console.error(error);
res.status(error.status || 500).end(error.message);
}
}
Try logging in and look for the state in the console,
{ returnTo: 'http://localhost:3000/dashboard' }
Cheers!

Cannot set headers after they are sent to the client with res.writeHead()

I am trying to redirect a user to the login if he isn't authenticated. I hardcoded the jwt for now. This works, but I only get an error saying Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client.
Since the function works I don't know what is wrong and couldn't really find an answer to it either. This is my code for reference:
function redirectUser(ctx, location) {
if (ctx.req) {
ctx.res.writeHead(302, { Location: location });
ctx.res.statusCode = 302;
ctx.res.setHeader(302, location);
ctx.res.end();
return { props: {} };
} else {
Router.push(location);
}
}
// getInitialProps disables automatic static optimization for pages that don't
// have getStaticProps. So article, category and home pages still get SSG.
// Hopefully we can replace this with getStaticProps once this issue is fixed:
// https://github.com/vercel/next.js/discussions/10949
MyApp.getInitialProps = async (ctx) => {
const jwt = false;
// Calls page's `getInitialProps` and fills `appProps.pageProps`
const appProps = await App.getInitialProps(ctx);
// Fetch global site settings from Strapi
const global = await fetchAPI("/global");
if (!jwt) {
if (ctx?.ctx.pathname === "/account") {
redirectUser(ctx.ctx, "/login");
}
}
// Pass the data to our page via props
return { ...appProps, pageProps: { global } };
};
Any help would be much appreciated.
The error "Error: Can't set headers after they are sent." means that you're already in the body, but some other function tried to set a header or statusCode. In your case it is the function ctx.res.setHeader(302, location); that's causing the issue.
After writeHead, the headers are baked in and you can only call res.write(body), and finally res.end(body).
You do not need to use setHeader when you are already using the writehead method.
Read here more about the writehead
So your redirectUser could be like :
function redirectUser(ctx, location) {
if (ctx.req) {
ctx.res.writeHead(302, { Location: location });
ctx.res.end();
return { props: {} };
} else {
Router.push(location);
}
}

Meteor accounts verifyEmail

I am trying to make email verification that with the accounts-password package work however I have come across a weird problem.
It seems the # in the email verification URL is causing an issue. The verification email URL usually looks like : http://localhost:3000/#/verify-email/cnaTqQSCgYAksIsFo5FgmV94NHwrfaM2g5GvdZDUMlN
When I click on this, nothing seems to happen; it just re-directs to localhost:3000/#
However when I remove the # (http://localhost:3000/verify-email/cnaTqQSCgYAksIsFo5FgmV94NHwrfaM2g5GvdZDUMlN) this seems to work perfectly.
The URL (http://localhost:3000/#/verify-email/cnaTqQSCgYAksIsFo5FgmV94NHwrfaM2g5GvdZDUMlN) comes from Meteor so it's not something I create.
Here are my routes and controllers (using iron-router)
Router.route('/verify-email/:_token', {
controller : 'AccountController',
action : 'verifyEmail'
});
AccountController = RouteController.extend({
fastRender: true,
data: function () {},
onBeforeAction: function () {
this.render('Loading');
this.next();
},
verifyEmail: function() {
var verificationToken = this.params._token;
console.log(verificationToken);
Accounts.verifyEmail(verificationToken, function(error) {
if (error) {
console.log(error);
} else {
Router.go('/');
}
});
}
});
Any help is appreciated.
The conflict might be connected to the accounts-password package together with iron:router as outlined here:
...add a server file that overrides the urls with # paths that Meteor creates, so that the Iron-Router can work:
(function () {
"use strict";
Accounts.urls.resetPassword = function (token) {
return Meteor.absoluteUrl('reset-password/' + token);
};
Accounts.urls.verifyEmail = function (token) {
return Meteor.absoluteUrl('verify-email/' + token);
};
Accounts.urls.enrollAccount = function (token) {
return Meteor.absoluteUrl('enroll-account/' + token);
};
})();
Hope it will guide you in the right direction.

Custom Meteor enroll template

In my application I want to seed the database with users and send them an enrollment link to activate their account (and choose a password). I also want them to verify/change some profile data.
On the server I seed the database like this:
Meteor.startup(function () {
if(Meteor.users.find().count() === 0) {
var user_id = Accounts.createUser({ email: 'some#email.com', profile: { some: 'profile' } });
Accounts.sendEnrollmentEmail(user_id);
}
})
The enrollment link is sent as expected, but I want to create a custom template for when the url in the email is clicked. Preferably handled by iron-router. (Not using the accounts-ui package).
I tried things like redirecting the user to a custom route like this:
var doneCallback, token;
Accounts.onEnrollmentLink(function (token, done) {
doneCallback = done;
token = token;
Router.go('MemberEnroll')
});
which is not working (it changes the url but not rendering my template)
I also tried to change the enroll URL on the server like this:
Accounts.urls.enrollAccount = function (token) {
return Meteor.absoluteUrl('members/enroll/' + token);
};
But when I do this, the Accounts.onEnrollmentLink callback does not fire.
Also, changing the URL is not documented so I'm not sure its a good practice at all.
Any help is appreciated.
In my application I'm doing like this
this.route('enroll', {
path: '/enroll-account/:token',
template: 'enroll_page',
onBeforeAction: function() {
Meteor.logout();
Session.set('_resetPasswordToken', this.params.token);
this.subscribe('enrolledUser', this.params.token).wait();
},
data: function() {
if(this.ready()){
return {
enrolledUser: Meteor.users.findOne()
}
}
}
})
As enrollment url is like this
http://www.yoursite.com/enroll-account/hkhk32434kh42hjkhk43
when users click on the link they will redirect to this template and you can render your template
In my publication
Meteor.publish('enrolledUser', function(token) {
return Meteor.users.find({"services.password.reset.token": token});
});
After taking the password from the user
Accounts.resetPassword(token, creds.password,function(e,r){
if(e){
alert("Sorry we could not reset your password. Please try again.");
}else{
alert("Logged In");
Router.go('/');
}
})
enroll link
Accounts.urls.enrollAccount = function (token) {
return Meteor.absoluteUrl('enroll-account/' + token);
};
Im afraid now isnt possible, what i did is changing the html and css using "rendered" function but it has some probs with delay
Meteor.startup(function(){
Template["_enrollAccountDialog"].rendered = function(){
document.getElementById('enroll-account-password-label').innerHTML = 'Escolha sua senha';
$('.accounts-dialog').css('background-color','#f4f5f5');
$('.accounts-dialog').css('text-align','center');
$('.accounts-dialog').removeAttr('width');
document.getElementById('login-buttons-enroll-account-button').className = ' create-account-button';
document.getElementById('login-buttons-enroll-account-button').innerHTML = 'Criar conta';
}
});

Return URL on verification

What sets the return URL for the verification email. Not the link that gets generated and inserted in the email, but when you click the link, it ends up going to a page on your site after its verified. How can I set what page it goes to?
You can set the URL by specifying Accounts.emailTemplates.verifyEmail.text. Here's an example:
Accounts.emailTemplates.siteName = 'MyApp';
Accounts.emailTemplates.from = 'me#example.com';
Accounts.emailTemplates.verifyEmail.subject = function() {
return 'Verify your email address on MyApp';
};
Accounts.emailTemplates.verifyEmail.text = function(user, url) {
var token = url.split('/').pop();
var verifyEmailUrl = Meteor.absoluteUrl("verify-email/" + token);
return verifyEmailEmailBody(verifyEmailUrl);
};
The callback takes a url parameter which is the default URL generated by meteor. You can extract the verification token and then use it to build a custom URL. The function needs to return a body string, which you'll generate by implementing verifyEmailEmailBody.
On the client, you'll need to set up the corresponding route. When the route is run, you can call Accounts.verifyEmail.
You can change the verification url used in the email and then handle that route yourself. Here I'll use /verify and redirect to /wherever if successful.
client
var match = window.location.pathname.match(/^\/verify\/(.*)$/);
var token;
if (match) {
token = match[1];
}
Meteor.startup(function () {
if (token) {
Accounts.verifyEmail(token, function (error) {
if (!error) {
window.location.pathname = '/wherever';
}
});
}
});
server
Accounts.urls.verifyEmail = function (token) {
return Meteor.absoluteUrl('verify/' + token);
};

Resources