Implement user login
log in is how users access your application after verifying their identity. For B2B applications, log in involves additional complexity as users may belong to multiple organizations and each organization may have different authentication requirements.
Here are some common log in use cases:
- Multiple authentication methods: Users can choose different authentication methods to log in, such as passwordless, their choice of social login, or enterprise SSO.
- Organization-enforced policies: Organization administrators can enforce log in policies, such as requiring specific authentication method for certain users or authenticating users through their identity provider.
- Cross-organization access: A user can log in to access multiple organizations or workspaces within your application.
- Invitation-based access: A user signs in for the first time after accepting an email invitation to join an organization.
- Enterprise SSO integration: Users authenticate through their corporate identity provider (e.g., Okta, Azure AD) instead of traditional username/password.
Scalekit helps you implement all such signup flows while handling the complexity of user management and authentication.
Let’s get started!
-
Install the Scalekit SDK
Section titled “Install the Scalekit SDK”npm install @scalekit-sdk/nodepip install scalekit-sdk-pythongo get -u github.com/scalekit-inc/scalekit-sdk-go/* Gradle users - add the following to your dependencies in build file */implementation "com.scalekit:scalekit-sdk-java:1.1.3"<!-- Maven users - add the following to your `pom.xml` --><dependency><groupId>com.scalekit</groupId><artifactId>scalekit-sdk-java</artifactId><version>1.1.3</version></dependency>Copy your API credentials from the Scalekit dashboard’s API Config section and set them as environment variables.
Terminal window SCALEKIT_ENVIRONMENT_URL='<YOUR_ENVIRONMENT_URL>'SCALEKIT_CLIENT_ID='<ENVIRONMENT_CLIENT_ID>'SCALEKIT_CLIENT_SECRET='<ENVIRONMENT_CLIENT_SECRET>'Create a new Scalekit client instance.
utils/auth.js import { Scalekit } from '@scalekit-sdk/node';export let scalekit = new Scalekit(process.env.SCALEKIT_ENVIRONMENT_URL,process.env.SCALEKIT_CLIENT_ID,process.env.SCALEKIT_CLIENT_SECRET);utils/auth.py from scalekit import ScalekitClient, AuthorizationUrlOptions, CodeAuthenticationOptionsimport osscalekit = ScalekitClient(os.environ.get('SCALEKIT_ENVIRONMENT_URL'),os.environ.get('SCALEKIT_CLIENT_ID'),os.environ.get('SCALEKIT_CLIENT_SECRET'))utils/auth.go package mainimport ("os""github.com/scalekit-inc/scalekit-sdk-go")var scalekit, err = scalekit.NewScalekitClient(os.Getenv("SCALEKIT_ENVIRONMENT_URL"),os.Getenv("SCALEKIT_CLIENT_ID"),os.Getenv("SCALEKIT_CLIENT_SECRET"),)if err != nil {panic(err)}utils/Auth.java import com.scalekit.ScalekitClient;import com.scalekit.Environment;public class Auth {public static ScalekitClient scalekit;static {Environment.configure(System.getenv("SCALEKIT_ENVIRONMENT_URL"),System.getenv("SCALEKIT_CLIENT_ID"),System.getenv("SCALEKIT_CLIENT_SECRET"));scalekit = new ScalekitClient(System.getenv("SCALEKIT_ENVIRONMENT_URL"),System.getenv("SCALEKIT_CLIENT_ID"),System.getenv("SCALEKIT_CLIENT_SECRET"));}} -
Redirect to the login page
Section titled “Redirect to the login page”Generate the authorization URL to redirect users to the Scalekit-hosted login page. To get a
refreshToken
for session management, ensure you includeoffline_access
in the scopes.Express.js const redirectUri = 'http://localhost:3000/api/callback';const options = {scopes: ['openid', 'profile', 'email', 'offline_access'],};const authorizationUrl = scalekit.getAuthorizationUrl(redirectUri, options);res.redirect(authorizationUrl);Flask from scalekit import AuthorizationUrlOptionsredirect_uri = 'http://localhost:3000/api/callback'options = AuthorizationUrlOptions()options.scopes = ['openid', 'profile', 'email', 'offline_access']authorization_url = scalekit.get_authorization_url(redirect_uri, options)# For web frameworks like Flask/Django:# return redirect(authorization_url)Gin redirectUri := "http://localhost:3000/api/callback"options := scalekit.AuthorizationUrlOptions{Scopes: []string{"openid", "profile", "email", "offline_access"},}authorizationUrl, err := scalekit.GetAuthorizationUrl(redirectUri, options)if err != nil {// handle error appropriatelypanic(err)}// For web frameworks like Gin:// c.Redirect(http.StatusFound, authorizationUrl.String())Spring import com.scalekit.internal.http.AuthorizationUrlOptions;import java.net.URL;import java.util.Arrays;String redirectUri = "http://localhost:3000/api/callback";AuthorizationUrlOptions options = new AuthorizationUrlOptions();options.setScopes(Arrays.asList("openid", "profile", "email", "offline_access"));URL authorizationUrl = scalekit.authentication().getAuthorizationUrl(redirectUri, options);This will redirect the user to Scalekit’s hosted sign-in page.
-
Handle user authentication
Section titled “Handle user authentication”Users can now log in using their preferred method. Scalekit handles the authentication details based on your configuration.
Configure Authentication Methods Learn how to enable and customize passwordless, social, and enterprise SSO login options. -
Retrieve user profile
Section titled “Retrieve user profile”After identity verification, Scalekit triggers a callback to your
redirect_uri
with an authorization code. Exchange this code to get the user’s profile.import scalekit from '@/utils/auth.js'const redirectUri = 'http://localhost:3000/api/callback';// Get the authorization code from the scalekit initiated callbackapp.get('/api/callback', async (req, res) => {const { code, error, error_description } = req.query;if (error) {return res.status(401).json({ error, error_description });}try {// Exchange the authorization code for a user profileconst authResult = await scalekit.authenticateWithCode(code, redirectUri);const { user } = authResult;// "user" object contains the user's profile information// Next step: Create a session and log in the user5 collapsed linesres.redirect('/dashboard/profile');} catch (err) {console.error('Error exchanging code:', err);res.status(500).json({ error: 'Failed to authenticate user' });}});6 collapsed linesfrom flask import Flask, request, redirect, jsonifyfrom scalekit import ScalekitClient, CodeAuthenticationOptionsapp = Flask(__name__)# scalekit imported from your auth utilsredirect_uri = 'http://localhost:3000/api/callback'@app.route('/api/callback')def callback():code = request.args.get('code')error = request.args.get('error')error_description = request.args.get('error_description')if error:return jsonify({'error': error, 'error_description': error_description}), 401try:# Exchange the authorization code for a user profileoptions = CodeAuthenticationOptions()auth_result = scalekit.authenticate_with_code(code, redirect_uri, options)user = auth_result.user# "user" object contains the user's profile information# Next step: Create a session and log in the user4 collapsed linesreturn redirect('/dashboard/profile')except Exception as err:print(f'Error exchanging code: {err}')return jsonify({'error': 'Failed to authenticate user'}), 5009 collapsed linespackage mainimport ("log""net/http""os""github.com/gin-gonic/gin""github.com/scalekit-inc/scalekit-sdk-go")// Create Scalekit client instancevar scalekitClient = scalekit.NewScalekitClient(os.Getenv("SCALEKIT_ENVIRONMENT_URL"),os.Getenv("SCALEKIT_CLIENT_ID"),os.Getenv("SCALEKIT_CLIENT_SECRET"),)const redirectUri = "http://localhost:3000/api/callback"func callbackHandler(c *gin.Context) {code := c.Query("code")errorParam := c.Query("error")errorDescription := c.Query("error_description")if errorParam != "" {c.JSON(http.StatusUnauthorized, gin.H{"error": errorParam,"error_description": errorDescription,})return}// Exchange the authorization code for a user profileoptions := scalekit.AuthenticationOptions{}authResult, err := scalekitClient.AuthenticateWithCode(code, redirectUri, options,)if err != nil {log.Printf("Error exchanging code: %v", err)c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to authenticate user",})return}user := authResult.User// "user" object contains the user's profile information// Next step: Create a session and log in the userc.Redirect(http.StatusFound, "/dashboard/profile")}10 collapsed linesimport com.scalekit.ScalekitClient;import com.scalekit.internal.http.AuthenticationOptions;import com.scalekit.internal.http.AuthenticationResponse;import org.springframework.web.bind.annotation.*;import org.springframework.web.servlet.view.RedirectView;import org.springframework.http.ResponseEntity;import org.springframework.http.HttpStatus;import java.util.HashMap;import java.util.Map;@RestControllerpublic class CallbackController {private final String redirectUri = "http://localhost:3000/api/callback";@GetMapping("/api/callback")public Object callback(@RequestParam(required = false) String code,@RequestParam(required = false) String error,@RequestParam(name = "error_description", required = false) String errorDescription) {if (error != null) {Map<String, String> errorResponse = new HashMap<>();errorResponse.put("error", error);errorResponse.put("error_description", errorDescription);return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(errorResponse);}try {// Exchange the authorization code for a user profileAuthenticationOptions options = new AuthenticationOptions();AuthenticationResponse authResult = scalekit.authentication().authenticateWithCode(code, redirectUri, options);var user = authResult.getUser();// "user" object contains the user's profile information// Next step: Create a session and log in the userreturn new RedirectView("/dashboard/profile");8 collapsed lines} catch (Exception err) {System.err.println("Error exchanging code: " + err.getMessage());Map<String, String> errorResponse = new HashMap<>();errorResponse.put("error", "Failed to authenticate user");return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);}}}The
authResult
contains the user’s profile and the necessary tokens to create a session. -
Create a user session
Section titled “Create a user session”With the user’s identity verified, you can now establish a session. This typically involves storing the tokens securely and using them to manage the user’s authenticated state.
const { accessToken, refreshToken, expiresIn } = authResult;// Store the refreshToken securely, e.g., in a databaseawait db.saveRefreshToken(user.id, refreshToken);// Set the accessToken as a secure, HTTP-only cookieres.cookie('accessToken', accessToken, {maxAge: (expiresIn - 60) * 1000,httpOnly: true,secure: process.env.NODE_ENV === 'production',sameSite: 'strict'});access_token = auth_result['access_token']refresh_token = auth_result.get('refresh_token')expires_in = auth_result.get('expires_in', 3600) # Default to 1 hour if not provided# Store the refresh_token securely, e.g., in a databaseawait db.save_refresh_token(user['id'], refresh_token)# Set the access_token as a secure, HTTP-only cookieresponse = redirect('/dashboard/profile')response.set_cookie('accessToken',access_token,max_age=expires_in - 60,httponly=True,secure=os.environ.get('FLASK_ENV') == 'production',samesite='Strict')return responseaccessToken := authResult.AccessTokenrefreshToken := authResult.RefreshTokenexpiresIn := authResult.ExpiresIn// Store the refreshToken securely, e.g., in a databaseerr = db.SaveRefreshToken(user.Id, refreshToken)if err != nil {c.JSON(500, gin.H{"error": "Failed to save refresh token"})return}// Set the accessToken as a secure, HTTP-only cookiec.SetCookie("accessToken",accessToken,expiresIn-60,"/","",os.Getenv("GIN_MODE") == "release",true,)String accessToken = authResult.getAccessToken();String refreshToken = authResult.getRefreshToken();int expiresIn = authResult.getExpiresIn();// Store the refreshToken securely, e.g., in a databaserefreshTokenRepository.save(user.getId(), refreshToken);// Set the accessToken as a secure, HTTP-only cookieCookie accessTokenCookie = new Cookie("accessToken", accessToken);accessTokenCookie.setMaxAge(expiresIn - 60);accessTokenCookie.setHttpOnly(true);accessTokenCookie.setSecure(System.getenv("SPRING_PROFILES_ACTIVE").equals("prod"));accessTokenCookie.setPath("/");response.addCookie(accessTokenCookie);
What’s next?
Section titled “What’s next?”You have successfully implemented the login flow. Now you can:
- Manage user sessions to handle token refreshes and expirations.
- Implement a secure logout flow.
- Explore the normalized user profile schema.