Allow users to change their passwords
caution
SuperTokens does not provide the UI for users to change/update their password, you will need to create the UI and setup a route on your backend to have this functionality.
In this section we will go over how you can create a route on your backend which can update a user's password. Calling this route will check if the old password is valid and update the user's profile with the new password.
/change-password
route#
Step 1: Creating the - You will need to create a route on your backend which is protected by the session verification middleware, this will ensure that only a authenticated user can access the protected route.
- To learn more about how to use the session verfication middleware for other frameworks click here
- NodeJS
- GoLang
- Python
- Other Frameworks
Important
For other backend frameworks, you can follow our guide on how to spin up a separate server configured with the SuperTokens backend SDK to authenticate requests and issue session tokens.
import { verifySession } from "supertokens-node/recipe/session/framework/express";
import { SessionRequest } from "supertokens-node/framework/express"
import express from "express";
let app = express();
app.post("/change-password", verifySession(), async (req: SessionRequest, res: express.Response) => {
// TODO: see next steps
})
import (
"net/http"
"github.com/supertokens/supertokens-golang/recipe/session"
)
// the following example uses net/http
func main() {
_ = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
// Wrap the API handler in session.VerifySession
session.VerifySession(nil, changePasswordAPI).ServeHTTP(rw, r)
})
}
func changePasswordAPI(w http.ResponseWriter, r *http.Request) {
// TODO: see next steps
}
# the following example uses flask
from supertokens_python.recipe.session.framework.flask import verify_session
from flask import Flask
app = Flask(__name__)
@app.route('/change-password', methods=['POST'])
@verify_session()
def change_password():
pass # TODO: see next steps
#
Step 2: Validate and update the user's password- You can now use
session
object to retrive the logged in user'suserId
. - Use the recipe's sign in function and check if the old password is valid
- Update the user's password.
- NodeJS
- GoLang
- Python
- Other Frameworks
Important
For other backend frameworks, you can follow our guide on how to spin up a separate server configured with the SuperTokens backend SDK to authenticate requests and issue session tokens.
// the following example uses express
import ThirdPartyEmailPassword from "supertokens-node/recipe/thirdpartyemailpassword";
import { verifySession } from "supertokens-node/recipe/session/framework/express";
import { SessionRequest } from "supertokens-node/framework/express"
import express from "express";
let app = express();
app.post("/change-password", verifySession(), async (req: SessionRequest, res: express.Response) => {
// get the supertokens session object from the req
let session = req.session
// retrive the old password from the request body
let oldPassword = req.body.oldPassword
// retrive the new password from the request body
let updatedPassword = req.body.newPassword
// get the user's Id from the session
let userId = session!.getUserId()
// get the signed in user's email from the getUserById function
let userInfo = await ThirdPartyEmailPassword.getUserById(userId)
if (userInfo === undefined) {
throw new Error("Should never come here")
}
// call signin to check that input password is correct
let isPasswordValid = await ThirdPartyEmailPassword.emailPasswordSignIn(userInfo.email, oldPassword)
if (isPasswordValid.status !== "OK") {
// TODO: handle incorrect password error
return
}
// update the user's password using updateEmailOrPassword
let response = await ThirdPartyEmailPassword.updateEmailOrPassword({
userId,
password: updatedPassword
})
if (response.status === "PASSWORD_POLICY_VIOLATED_ERROR") {
// TODO: handle incorrect password error
return
}
// TODO: send successful password update response
})
import (
"encoding/json"
"net/http"
"github.com/supertokens/supertokens-golang/recipe/session"
"github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword"
)
func main() {
_ = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
// Wrap the API handler in session.VerifySession
session.VerifySession(nil, changePasswordAPI).ServeHTTP(rw, r)
})
}
type RequestBody struct {
OldPassword string
NewPassword string
}
func changePasswordAPI(w http.ResponseWriter, r *http.Request) {
// retrieve the session object as shown below
sessionContainer := session.GetSessionFromRequestContext(r.Context())
// retrive the old password from the request body
var requestBody RequestBody
err := json.NewDecoder(r.Body).Decode(&requestBody)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// get the userId from the session
userID := sessionContainer.GetUserID()
// get the signed in user's email from the getUserById function
userInfo, err := thirdpartyemailpassword.GetUserById(userID)
if err != nil {
// TODO: Handle error
return
}
// call signin to check that the input is correct
isPasswordValid, err := thirdpartyemailpassword.EmailPasswordSignIn(userInfo.Email, requestBody.OldPassword)
if err != nil {
// TODO: Handle error
return
}
if isPasswordValid.OK != nil {
// TODO: Handle error
return
}
updateResponse, err := thirdpartyemailpassword.UpdateEmailOrPassword(userID, &userInfo.Email, &requestBody.NewPassword, nil)
if err != nil {
// TODO: Handle error
return
}
if updateResponse.PasswordPolicyViolatedError != nil {
// This error is returned if the new password doesn't match the defined password policy
// TODO: Handle error
return
}
// TODO: send successful password update response
}
from supertokens_python.recipe.session.framework.flask import verify_session
from supertokens_python.recipe.session import SessionContainer
from supertokens_python.recipe.thirdpartyemailpassword.syncio import get_user_by_id, emailpassword_sign_in, update_email_or_password
from supertokens_python.recipe.thirdpartyemailpassword.interfaces import EmailPasswordSignInWrongCredentialsError, UpdateEmailOrPasswordPasswordPolicyViolationError
from flask import g
@app.route('/change-password', methods=['POST'])
@verify_session()
def change_password():
session: SessionContainer = g.supertokens
# get the userId from the session object
user_id = session.get_user_id()
# get the signed in user's email from the getUserById function
users_info = get_user_by_id(user_id)
if users_info is None:
raise Exception("Should never come here")
# call signin to check that the input password is correct
isPasswordValid = emailpassword_sign_in(users_info.email, request.json["oldPassword"])
if isinstance(isPasswordValid, EmailPasswordSignInWrongCredentialsError):
# TODO: handle incorrect password error
return
# update the users password
update_response = update_email_or_password(user_id, password=request.json["newPassword"])
if isinstance(update_response, UpdateEmailOrPasswordPasswordPolicyViolationError):
# TODO: handle password policy violation error
return
# TODO: send successful password update response
#
Step 3: Revoke all sessions associated with the user (optional)- Revoking all sessions associated with the user will force them to reauthenticate with their new password.
- NodeJS
- GoLang
- Python
- Other Frameworks
Important
For other backend frameworks, you can follow our guide on how to spin up a separate server configured with the SuperTokens backend SDK to authenticate requests and issue session tokens.
// the following example uses express
import ThirdPartyEmailPassword from "supertokens-node/recipe/thirdpartyemailpassword";
import Session from "supertokens-node/recipe/session";
import { verifySession } from "supertokens-node/recipe/session/framework/express";
import { SessionRequest } from "supertokens-node/framework/express"
import express from "express";
let app = express();
app.post("/change-password", verifySession(), async (req: SessionRequest, res: express.Response) => {
let userId = req.session!.getUserId();
/**
*
* ...
* see previous step
* ...
*
* */
// revoke all sessions for the user
await Session.revokeAllSessionsForUser(userId)
// revoke the current user's session, we do this to remove the auth cookies, logging out the user on the frontend.
await req.session!.revokeSession()
// TODO: send successful password update response
})
import (
"net/http"
"github.com/supertokens/supertokens-golang/recipe/session"
)
func main() {
_ = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
// Wrap the API handler in session.VerifySession
session.VerifySession(nil, changePasswordAPI).ServeHTTP(rw, r)
})
}
type ResponseBody struct {
OldPassword string
NewPassword string
}
func changePasswordAPI(w http.ResponseWriter, r *http.Request) {
// retrieve the session object as shown below
sessionContainer := session.GetSessionFromRequestContext(r.Context())
userID := sessionContainer.GetUserID()
/**
*
* ...
* see previous step
* ...
*
* */
// revoke all sessions for the user
_, err := session.RevokeAllSessionsForUser(userID)
if err != nil {
// TODO: Handle error
}
// revoke the user's current session, we do this to remove the auth cookies, logging out the user on the frontend
err = sessionContainer.RevokeSession()
if err != nil {
// TODO: Handle error
}
// TODO: send successful password update response
}
from supertokens_python.recipe.session.framework.flask import verify_session
from supertokens_python.recipe.session.syncio import revoke_all_sessions_for_user
from flask import Flask
from supertokens_python.recipe.session import SessionContainer
app = Flask(__name__)
@app.route('/change-password', methods=['POST'])
@verify_session()
def change_password():
session: SessionContainer = g.supertokens
# get the userId from the session object
user_id = session.get_user_id()
# TODO: see previous step...
# revoke all sessions for the user
revoke_all_sessions_for_user(user_id)
# revoke the user's current session, we do this to remove the auth cookies, logging out the user on the frontend
session.sync_revoke_session()
# TODO: send successful password update response