Integrate a Portal with your app login
If you haven't already done so please check out our Portal Quickstart guide to get your API integrated with Speakeasy before continuing with this guide.
Your Speakeasy powered DevEx portal will need a way to authenticate users. This guide will walk you through the steps to integrate your portal with your existing app's login. Currently Speakeasy does not provide a fully managed login solution but will look to do so in the near future).
Dev Portal Login Callback
When setting up the developer portal you will be prompted to provide a "Login Callback URL". This is the URL that Speakeasy will redirect the user to confirm login. It will also
be used to provide a customer_id
for each user. This allows us to power dev portal features while being aware of but not coupled to your customer tenancy model.
.
Login Flow
A complete login flow may look like this:
- User arrives in the Speakeasy Portal and, because they are not logged in to the portal, is directed to a Login page on your site
- User logs in (or is already logged in)
- Your site creates a JWT redirect URL with the user’s information.
- User is redirected to the Speakeasy DevEx Portal and logged in to Speakeasy.
Some pages of the developer portal do not require authentication to view (e.g. client sdks). The login redirect is automatic if no non-authenticated pages are enabled; the user will be presented with a login button that will trigger the redirect if only pages that require authentication are enabled.
Once your API User is sent from Speakeasy’s Portal to your company’s site, you’ll need to redirect the users back to Speakeasy with a
token created by the getPortalLoginToken
method on the Speakeasy serverside SDK.
This delegates both Authentication and Authorization to your existing mechanism. You can choose to only allow some users access to the Speakeasy DevEx Portal, via introducing guarding logic
around the getPortalLoginToken
that will be executed by your application server.
Tenancy is expected to be provided by configuring the customer_id
claim in the getPortalLoginToken
API call. Each customer_id
has an isolated developer portal view.
This customer_id
could be an email (in an email-per-tenant model), or an ID that is shared by multiple users in a tenant. Note: This customer_id
corresponds
to the customer_id
assigned by the setCustomerId
method on the Speakeasy serverside SDK when ingesting requests.
Example Callback Implementation
As an example, here is the way this could be built with a <LoginRedirect>
component that would wrap the root of a react application.
- Typescript
- Java
import { Navigate, useSearchParams } from "react-router-dom";
import { useEffect, useState } from "react";
import { getSpeakeasyPortalLoginToken } from "api";
const LoginRedirect = () => {
useEffect(() => {
// When a callbackUrl is supplied, attempt a redirect.
if (searchParams.has("callbackUrl")) {
const callbackUrl = searchParams.get("callbackUrl")!!;
getRedirectUrl(callbackUrl).then(
(url) => {
if (url) {
window.location.replace(url);
}
searchParams.delete("callbackUrl");
setSearchParams(searchParams);
}
);
}
}, [searchParams]);
if (!redirect) {
return <></>;
}
return <Navigate to={redirect} />;
};
const getRedirectUrl = async (
callbackUrl: string,
) => {
const host = new URL(callbackUrl).host;
if (host.endsWith("your-company-name.portal.speakeasyapi.dev")) {
try {
const token = await getSpeakeasyPortalLoginToken();
const target = new URL(callbackUrl);
target.searchParams.set("speakeasyAccessToken", token.access_token);
return target;
} catch (error) {
console.warn(
"There was a problem redirecting to the callback URL.",
error
);
}
}
return "";
};
export default LoginRedirect;
@Get("/speakeasy_portal_login_token")
public String getPortalLoginAccessToken(@RequestAttribute(SpeakeasyMiddlewareController.Key) SpeakeasyMiddlewareController controller){
String customerId = "some-customer-id";
// Populate with any custom claims you want added to your created API keys
Map<String, String> jwtCustomClaims = new HashMap<>();
jwtCustomClaims.put("sub", "your-desired-sub")
jwtCustomClaims.put("user_id", "your-desired-user_id")
jwtCustomClaims.put("email", "your-desired-email")
// Populate with any permissions you want enabled/disabled for the user
Map<String, Boolean> permissions = new HashMap<>();
permissions.put("end_user:api_keys:read", true);
permissions.put("end_user:api_keys:write", true)
String accessToken = controller.getPortalLoginToken(customerId, "Some User Display Name", jwtCustomClaims,
permissions, filterBuilder.build());
// build response
}
If you are managing Customer API keys through Speakeasy then you will need to create a portal access token through the serverside SDK integration. More information on this here.