Skip to main content

Client setup

Prerequisites

Each client connecting to the SSO needs to be set up separately.

For each client you will obtain following information:

  • clientId - needed for calling the SSO client API
  • clientSecret - needed for calling the SSO client API
  • authHandlerUri - endpoint which is used by the frontend SDK

Once you have all of these information you can set up your frontend client.

Proxying the authHandlerUri

In order for the Citadel Web SDK to work properly you need to proxy the authHandlerUri so the cookie based SSO can work properly.

For example if your web client is hosted at example.com you need to setup the proxy for authHandlerUri at example.com/auth.

You need to forward all the headers, all the cookies and all the query parameters which hit the /auth endpoint. In addition you need to set X-Forwarded-Host to actual Host header value.

Once you do this the Citadel Web SDK starts work out of box.

In next section we provide you with a few examples of how to properly proxy the /auth path.

Proxy with CloudFront

Here is the example in CDK of how to set up the correct proxy for the authHandlerUri:

// the real URL needs to be provided here
const authUrl = "{id}.lambda-url.{region}.on.aws";

// following behaviour needs to be added to your CloudFront Distribution instance
const cloudFrontBehaviour: CloudFrontBehaviorParams = [
"/auth",
new HttpOrigin(Fn.parseDomainName(authUrl)),
{
allowedMethods: AllowedMethods.ALLOW_ALL,
cachePolicy: new CachePolicy(this, "ApiCachePolicy", {
comment: "API cache policy",
minTtl: Duration.seconds(0),
maxTtl: Duration.seconds(1), // Cannot set to 0 due bug in CloudFront. Cookies are not passed to/from origin when all TTLs are set to 0.
defaultTtl: Duration.seconds(0),
// this is minimal header configuration and can change in future - if possible forward all headers
headerBehavior: CacheHeaderBehavior.allowList(
"Sec-Fetch-Mode",
"Sec-Fetch-Site",
"Sec-Fetch-Dest",
"Sec-Fetch-User",
"Referer",
"X-Forwarded-Host"
),
cookieBehavior: CacheCookieBehavior.all(),
queryStringBehavior: CacheQueryStringBehavior.all(),
enableAcceptEncodingBrotli: true,
enableAcceptEncodingGzip: true,
}),
compress: false,
viewerProtocolPolicy: ViewerProtocolPolicy.HTTPS_ONLY,
functionAssociations: [
{
function: new CloudFrontFunction(this, "AuthViewerRequest", {
code: FunctionCode.fromInline(`
function handler(event) {
event.request.headers['x-forwarded-host'] = { value: event.request.headers.host.value };
return event.request;
}
`),
}),
eventType: FunctionEventType.VIEWER_REQUEST,
},
],
},
];
note

The CloudFront cannot be set to forward all of the headers hence we provide the minimal list of headers which needs to be forwarded. This list may change in the future.

Proxy with Nginx

Here is the example of proxying the authHandlerUri with Nginx. You will need to change the {id}.lambda-url.{region}.on.aws:

server {
listen 443 ssl;
server_name example.com;

ssl_certificate /path/to/your/certificate.crt;
ssl_certificate_key /path/to/your/privatekey.key;

location /auth {
proxy_pass {authHandlerUri}$request_uri;
proxy_set_header Host $proxy_host;
proxy_set_header X-Forwarded-Host $host;

proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 600s;

# Preserve original headers, cookies, and query strings
proxy_pass_request_headers on;
proxy_pass_request_body on;
}
}
note

Do not forget to replace {authHandlerUri} with the real URL similar to https://{id}.lambda-url.{region}.on.aws

Proxy with Apache

Here is the example of proxying the authHandlerUri with Apache. You will need to change the {id}.lambda-url.{region}.on.aws:

<VirtualHost *:443>
ServerName example.com

SSLEngine on
SSLCertificateFile /path/to/your/certificate.crt
SSLCertificateKeyFile /path/to/your/privatekey.key

ProxyPreserveHost On

<Location "/auth">
ProxyPass "https://{authHandlerUri}/"
ProxyPassReverse "https://{authHandlerUri}/"

# Forward necessary headers
RequestHeader set X-Forwarded-Host "example.com"
RequestHeader set X-Forwarded-Proto "https"
RequestHeader setifempty Sec-Fetch-Mode "%{Sec-Fetch-Mode}e"
RequestHeader setifempty Sec-Fetch-Site "%{Sec-Fetch-Site}e"
RequestHeader setifempty Sec-Fetch-Dest "%{Sec-Fetch-Dest}e"
RequestHeader setifempty Sec-Fetch-User "%{Sec-Fetch-User}e"
RequestHeader setifempty Referer "%{Referer}i"

RequestHeader set Host "{authHandlerUri}"

</Location>

# specify your other rules here

</VirtualHost>
note

Do not forget to replace {authHandlerUri} with the real URL similar to https://{id}.lambda-url.{region}.on.aws

Citadel Web SDK

The JavaScript SDK for use with web clients is provided and can be downloaded from npm:

npm i @everlution/citadel-web-sdk

Once you have installed the Citadel Web SDK you can easily use it like this:

import { CitadelClient } from "@everlution/citadel-web-sdk";

export const sso = new CitadelClient();

// Check is user is signed-in on the current domain/app:
if (!sso.hasLocalSession()) {
sso.signIn({ redirect: new URL(new URL(window.location.href).origin) });
} else {
console.log("already have session");

// you can sign out user by using
// sso.signOut()
}

When the sso.signIn() function is called the user is redirected to the identity page where user is going to be authenticated by selected authentication method (for example username and password). Once the authentication is successful the user is redirected back to the client and the HTTP only cookie is set inside the browser.

The browser will now add the cookie header to each request automatically so you can authorize your users on the server with provided backend SDKs which are described in Server setup section.

We recommend to host your APIs on the same domain as per client. This is the most secure way of connecting to your API since the cookie is not accessible via JavaScript and cannot be misused by any 3rd party JavaScript package.

tip

You can proxy your API exactly same as proxying the authHandlerUri. Please review the above section for the inspiration.

If for any reason you are unable to proxy the requests to your API through the same domain as the client resides, we provide an escape hatch - generation of bearer tokens.

warning

Using the bearer tokens with your API is less secure because any 3rd party can potentially generate the bearer token through the JavaScript. The generated tokens are short lived (usually not more than 30 seconds) and signed by server so they can be verified when used on server.

We recommend to put your API behind the same domain as your client in order to avoid this pitfall.

You can easily generate the bearer token through Citadel Web SDK like this:

import { CitadelClient } from "@everlution/citadel-web-sdk";

export const sso = new CitadelClient();

if (sso.hasLocalSession()) {
// token is short lived so you should generate new token before each API call
const token = await sso.generateBearerToken();
}
note

You should generate new bearer token before each call of your API since the tokens are short lived.

Client settings

For each individual client you can set following options:

  • maxSessionAge - seconds passed before session expires
  • validRedirectUrls - list of URIs where user can be redirected after the authentication is successful