FusionAuth is, at its core, an architectural component of a larger application. I like to say “no one ever builds a full application with just FusionAuth”. Sure, your application needs authentication and authorization, but there are other critical components. For example, the features of your application that make users want to register and log in.
But here at FusionAuth, we want simple, secure authentication to be easy to add to any application. FusionAuth can run anywhere, but what use is that if it isn’t easy to integrate? Applications which FusionAuth works with include:
- JavaScript single page applications (SPAs)
- traditional webapps
- commercial off-the-shelf applications
- server-side applications
- APIs
- mobile apps
Pretty much anything which can use OpenID Connect (OIDC), Security Assertion Markup Language (SAML), or an HTTP based API should work. You can see our list of quickstarts for integration examples.
Recently, the team built software development kits (SDKs) to make it even easier for SPA developers. The SDKs support the following JavaScript front-end frameworks:
This post will cover the nitty gritty of using these SDKs, but first, let’s discuss the difference between an SDK and a client library.
Client Libraries vs SDKs
At FusionAuth, an SDK is different from a client library, even though many people in the industry use the terms interchangeably.
SDKs
An SDK is an opinionated collection of abstracted functionality. If it were a toy, it’d be a doll or a toy truck. Ready to play with, no assembly required. SDKs are designed to allow front-end engineers to add authentication workflows to their applications.
A FusionAuth SDK includes:
- a button and JavaScript function for logging a user in
- a button and JavaScript function for logging a user out
- a button and JavaScript function for user registration
- filters to lock down content by role or authentication state
- token refresh and management functionality
SDKs are configured to work with a specific FusionAuth application and therefore expect a client Id as well as a FusionAuth instance URL. The SDKs do not offer management functionality and therefore don’t require an API key.
Client Libraries
A client library, in contrast, is a wrapper over FusionAuth APIs. Client libraries support all public FusionAuth API endpoints and offer a convenient way to use that API in many software languages. At the time of writing, there are eight supported languages, including TypeScript. There’s also an OpenAPI definition, useful for viewing API changes over time or generating a client library for a language without an official client library, like C++ or Rust.
If you want to compare a client library to a child’s toy, it is like legos. Engineers build a script or program using a client library to extend or configure FusionAuth. Some examples of tasks well suited to a client library:
- rotating client secrets or API keys to improve your security posture
- pulling user data every night for data warehouse ingestion
- creating a new FusionAuth tenant every time a client signs up for your app
- offering a unique, custom login or registration experience not supported by the FusionAuth hosted login pages
Requests made with client libraries usually require an API key as well as a URL for the FusionAuth instance to which the requests will be applied.
Better Together?
You can use both an SDK and a client library if your use case requires front-end SDK-based solutions for easy integration and the management access provided by a client library. They are not mutually exclusive.
SDK Examples
The FusionAuth SPA quickstarts use SDKs when available. Let’s look at some code from these sample integrations:
Here’s a component from the Angular quickstart.
import { Component, OnDestroy, OnInit, inject } from '@angular/core';
import { FusionAuthService, UserInfo } from '@fusionauth/angular-sdk';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit, OnDestroy {
private fusionAuthService: FusionAuthService = inject(FusionAuthService);
isLoggedIn: boolean = this.fusionAuthService.isLoggedIn();
userInfo: UserInfo | null = null;
isGettingUserInfo: boolean = false;
subscription?: Subscription;
ngOnInit(): void {
if (this.isLoggedIn) {
this.subscription = this.fusionAuthService
.getUserInfoObservable({
onBegin: () => (this.isGettingUserInfo = true),
onDone: () => (this.isGettingUserInfo = false),
})
.subscribe({
next: (userInfo) => (this.userInfo = userInfo),
error: (error) => console.error(error),
});
}
}
ngOnDestroy(): void {
this.subscription?.unsubscribe();
}
logout() {
this.fusionAuthService.logout();
}
login() {
this.fusionAuthService.startLogin();
}
}
Here’s an example dynamic header from the React quickstart.
import { useFusionAuth } from "@fusionauth/react-sdk";
import changebankLogo from "../assets/changebank.svg";
export default function LogoHeader() {
const { isLoggedIn, userInfo, startLogin, startLogout } = useFusionAuth();
return (
<div id="logo-header">
<img src={changebankLogo} alt="Change Bank" width="259" height="70" />
{isLoggedIn ? (
<div className="h-row">
<p className="header-email">{userInfo?.email}</p>
<button
className="button-lg"
style={{ cursor: "pointer" }}
onClick={() => startLogout()}
>
Logout
</button>
</div>
) : (
<button
className="button-lg"
style={{ cursor: "pointer" }}
onClick={() => startLogin("state-from-login")}
>
Login
</button>
)}
</div>
);
}
And, finally, here’s code from the Vue quickstart showing <RequireAuth>
usage which controls access to user interface elements based on whether someone has authenticated.
<template>
<div id="logo-header">
<img src="@/assets/changebank.svg" alt="Change Bank" width="259" height="70"/>
<RequireAuth>
<div class="h-row">
<p class="header-email" v-if="userInfo?.email">
{{ userInfo.email }}
</p>
<a class="button-lg" style="cursor: pointer" @click="logout()">Logout</a>
</div>
</RequireAuth>
<RequireAnonymous>
<a class="button-lg" style="cursor: pointer" @click="login()">Login</a>
</RequireAnonymous>
</div>
</template>
<script setup lang="ts">
import {useFusionAuth} from "@fusionauth/vue-sdk";
const {userInfo, login, logout} = useFusionAuth();
</script>
More about SDKs
In general, there are two components for each SDK.
- a client-side package installed into your SPA via a package manager like npm
- an optional server-side component
The SDKs are open source and the GitHub repos are linked from the documentation pages, so you can dig in there if you want more details.
The Client Side Package
The client-side package offers, as mentioned above, pre-built buttons or functions to which you can attach UI elements. Each of these performs a redirect to FusionAuth’s hosted login pages for the corresponding operation; this includes the Authorization Code grant for login.
This is the recommended approach for two reasons:
- more extensive functionality
- increased security
Let’s discuss each of these in turn.
Functionality
When you use the Authorization Code grant for authentication, as well as redirects for registration and logout, you are leveraging the FusionAuth provided functionality. Your application immediately supports a large number of authentication workflows. These include, but are not limited to:
- forgot password
- email verification
- MFA
- magic link authentication
- password expiration
- enterprise single sign-on (SAML, OIDC)
- social sign-on (Google, Facebook, etc)
- profile management (with a paid license)
- passkeys (with a paid license)
- and more
Now, it’s not entirely effortless. You must configure FusionAuth, but all this functionality is present, tested, and maintained over time. For example, instead of having to figure out how to set up email verification, you configure a customizable email and enable it in FusionAuth, and it works. In addition, browser redirects work with any language, framework, or browser. This ensures your application’s login page is never a barrier to your users.
Security
When using a redirect, the only application that sees or holes sensitive user credentials is FusionAuth. Your application code never has access to these, which decreases the attack surface area.
In addition, FusionAuth has built-in security features such as:
- the ability to lock user accounts
- password rules
- webhook notifications when a user has failed to login
- breached password notification (with a paid license)
- rate limiting of sensitive security related actions (with an Enterprise license)
- and more
FusionAuth takes security seriously; it’s our full-time job. By redirecting to FusionAuth, you don’t have to worry about any other JavaScript in your application getting access to sensitive information or credentials.
Look and Feel
Additional functionality and security are great, but you typically want your login pages to look like your application. Doesn’t a browser redirect make that difficult?
Well, no. FusionAuth allows complete control over the look and feel of the hosted login pages, including adding or removing HTML, CSS, images, and JavaScript to the pages. Learn more about this functionality, which we call themes, including an example using Tailwind CSS.
One other common objection to redirection is the ability to send a successfully authenticated user right back to where they started. It’s common to allow users to log in from multiple states in your SPA, after all. Luckily, you can use the state
parameter to deep link from any page in your SPA to FusionAuth and then send the user right back to that page after a successful login. Here’s a video displaying that:
Here’s code from an example application that extracts a URL from a state
parameter and returns the user to that URL after a successful exchange at the token endpoint.
const stateFromServer = req.query.state;
const stateValue = stateFromServer.split('-')[0];
const urlToEndUpAt = stateFromServer.split('-')[1];
if (stateValue !== req.session.stateValue) {
console.log("State doesn't match. uh-oh.");
console.log("Saw: " + stateFromServer + ", but expected: " + req.session.stateValue);
res.redirect(302, '/');
return;
}
// This code stores the user in a server-side session
client.exchangeOAuthCodeForAccessTokenUsingPKCE(req.query.code,
clientId,
clientSecret,
'http://localhost:3000/oauth-redirect',
req.session.verifier)
.then((response) => {
return client.retrieveUserUsingJWT(response.response.access_token);
})
.then((response) => {
if (!response.response.user.registrations || response.response.user.registrations.length == 0 || (response.response.user.registrations.filter(reg => reg.applicationId === clientId)).length == 0) {
console.log("User not registered, not authorized.");
res.redirect(302, '/');
return;
}
req.session.user = response.response.user;
})
.then((response) => {
res.redirect(302, urlToEndUpAt);
}).catch((err) => {
console.log("in error");
console.error(JSON.stringify(err));
});
The takeaway from this code is that you can set the state
parameter when redirecting the user to the login page, then extract a value from it and redirect back to the correct location.
The Server Side Component
The server-side component has endpoints that correspond to the hosted backend APIs. You can implement the server-side component in any other language if that’s preferable for your application. The server-side code sets access tokens and refresh tokens as HttpOnly
, Secure
cookies. This is a great storage choice for first-party API requests because such cookies are not vulnerable to JavaScript exfiltration. The cookies are presented on every request from the SPA to an API, as long as you set the credentials
property correctly and the API is in the same domain that the SPA is served from. For this approach to work, APIs must look for an access token in a cookie, though they can also fall back to the Authorization
header. You can see examples of this behavior in our API quickstarts.
For SPAs that must request APIs that don’t live on the same domain the SPA is served from, cookies won’t work. In this situation, the backend for front-end pattern is recommended. You should not put bearer tokens into local storage or session storage. Here’s a video about the insecurity of storing tokens anyplace accessible to JavaScript running in the browser.
You can read more about OAuth token storage in this article. Using a server-side component gives you flexibility around token storage.
Redirects Don’t Work, Help!
If your application architecture, use case, or design team rules out redirects, you can use the Login API to directly pass user credentials to FusionAuth. You’ll miss out on the functionality and security benefits outlined above, however.
This is not a recommended approach and at present there’s no SDK support for this approach. However, FusionAuth users can and have built out their own login interfaces using this API. If you pursue this, use tightly scoped API keys and make sure you understand the security ramifications of a publicly accessible API key.
In Conclusion
Using the Angular, React, or Vue SDKs can accelerate your app’s integration with FusionAuth.
By doing so, you can focus on the features your users actually care about while leveraging a world-class implementation of login workflows and the security of an isolated authentication server.
Happy SDKing!