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.
Step 1: Creating the /change-password route#
- 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
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 sessionobject 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
// the following example uses express
import EmailPassword from "supertokens-node/recipe/emailpassword";
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 EmailPassword.getUserById(userId)
    if (userInfo === undefined) {
        throw new Error("Should never come here")
    }
    // call signin to check that input password is correct
    let isPasswordValid = await EmailPassword.signIn(userInfo.email, oldPassword)
    if (isPasswordValid.status !== "OK") {
        // TODO: handle incorrect password error
        return
    }
    // update the user's password using updateEmailOrPassword
    let response = await EmailPassword.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/emailpassword"
)
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 := emailpassword.GetUserByID(userID)
    if err != nil {
        // TODO: Handle error
        return
    }
    // call signin to check that the input is correct
    isPasswordValid, err := emailpassword.SignIn(userInfo.Email, requestBody.OldPassword)
    if err != nil {
        // TODO: Handle error
        return
    }
    if isPasswordValid.OK != nil {
        // TODO: Handle error
        return
    }
    updateResponse, err := emailpassword.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.emailpassword.syncio import get_user_by_id, sign_in, update_email_or_password
from supertokens_python.recipe.emailpassword.interfaces import SignInWrongCredentialsError, 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 = sign_in(users_info.email, request.json["oldPassword"]) 
    if isinstance(isPasswordValid, SignInWrongCredentialsError):
        # 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
// the following example uses express
import EmailPassword from "supertokens-node/recipe/emailpassword";
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