Customize Consent Preferences

We use cookies to help you navigate efficiently and perform certain functions. You will find detailed information about all cookies under each consent category below.

The cookies that are categorized as "Necessary" are stored on your browser as they are essential for enabling the basic functionalities of the site. ... 

Always Active

Necessary cookies are required to enable the basic features of this site, such as providing secure log-in or adjusting your consent preferences. These cookies do not store any personally identifiable data.

Functional cookies help perform certain functionalities like sharing the content of the website on social media platforms, collecting feedback, and other third-party features.

Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics such as the number of visitors, bounce rate, traffic source, etc.

Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.

Advertisement cookies are used to provide visitors with customized advertisements based on the pages you visited previously and to analyze the effectiveness of the ad campaigns.

Other uncategorized cookies are those that are being analyzed and have not been classified into a category as yet.

Skip to main content

Integrate Strong Customer Authentication (SCA)

If you want to embed Swan into your own mobile app, you'll need to integrate Strong Customer Authentication (SCA) using in-app browsers. This way, your users will have the security of Swan's SCA directly from your app.

In-app browser required

For security reasons, all links must open in in-app browsers.
Therefore, never use WebViews to open URLs (consentUrl or oAuthUrl).

Overview

To integrate Swan's Strong Customer Authentication (SCA), you'll need:

  1. An in-app browser package.
  2. A custom URL scheme with a deep link listener package.
  3. A backend server (this guide uses https://my-company-api.com).

Check out Swan's demo app GitHub repository for an integration example.

Step 1: Set up your in-app browser

Use the documentation for your system's recommended API to set up your in-app browser.

SystemAPIReference
AndroidCustom Tabs APISwan implementation
iOSSFSafariViewController APISwan implementation

We provide an official React Native package: @swan-io/react-native-browser

Follow the references

When using frameworks other than React Native, ensure that your package is configured in a way that follows Swan's implementation references.

Use the documentation for your system's recommended tutorial to set up the deep link listener for your app.

SystemRecommended guide
AndroidCreate deep links to app content
iOSDefining a custom URL scheme for your app

If you are using React Native, follow this guide.

Step 3: Configure the sign-in process

This five-step process describes an ideal path for your users to sign in.

3.1 Sign-in button

First, the user clicks a sign-in button in your app. Clicking this button opens an in-app browser and starts listening for deep links.

Linking.addListener("url", ({ url }) => {
// called when deep link is visited
});

// …

openBrowser("https://my-company-api.com/auth").catch((error) => {
console.error(error);
});

3.2 Authorization URL

Next, your server constructs a Swan authorization URL and redirects the user.

app.get("/auth", async (req, reply) => {
const params = querystring.encode({
client_id: "$YOUR_CLIENT_ID",
response_type: "code",
redirect_uri: "https://my-company-api.com/auth/callback", // must be registered in our dashboard
scope: ["openid", "offline"].join(" "),
state: generateUserState(req),
});

return reply.redirect(`https://oauth.swan.io/oauth2/auth?${params}`);
});

3.3 User logs in

The user logs in and is redirected to https://my-company-api.com/auth/callback.

3.4 Server responds

Your server:

  1. Reads the authorization code.
  2. Exchanges it for a user access token.
  3. Stores the token using a unique identifier.
  4. Redirects the user to a deep link.
app.get("/auth/callback", async (req, reply) => {
if (req.query.state !== generateUserState(req)) {
throw new Error("Something isn't right, abort");
}

const token = await client.post("https://oauth.swan.io/oauth2/token", {
client_id: "$YOUR_CLIENT_ID",
client_secret: "$YOUR_CLIENT_SECRET",
grant_type: "authorization_code",
code: req.query.code,
redirect_uri: "https://my-company-api.com/auth/callback",
});

const sessionId = randomUUID();
await storeTokenInKeyValueStore(sessionId, token);

return reply.redirect(`com.company.myapp://close?sessionId=${sessionId}`);
});

3.5 Close browser and store identifier

Finally, the listener closes the in-app browser and stores the identifier.

Linking.addListener("url", ({ url }) => {
// called when deep link is visited

if (url.startsWith("com.company.myapp://close")) {
closeBrowser(); // required on iOS

const { query } = parseUrl(url);

if (query.sessionId) {
AsyncStorage.setItem("sessionId", query.sessionId);
}
}
});

// …

openBrowser("https://my-company-api.com/auth").catch((error) => {
console.error(error);
});

This six-step process describes an ideal path for your users to consent. This example uses the action of creating a card.

It's important that, when clicked, consent links respect one of the following behaviors:

  • Option 1: The link opens a pop-up that redirects the user to another page. Upon redirect, the pop-up closes automatically.
  • Option 2: The link opens within the same page, then redirects the user to the rest of the flow.

First, the user clicks a Create a card button in your app. Your server API receives the call with the following sessionId header.

const sessionId = await AsyncStorage.getItem("sessionId");

await client.post("https://my-company-api.com/add-card", {
headers: { sessionId },
});

Your server calls the Swan API with a user access token and returns the consentUrl.

app.get("/add-card", async (req, reply) => {
const token = await getTokenFromKeyValueStore(req.headers.sessionId);

const { consentUrl } = await swanClient.addCard(
{
input: {
// …
consentRedirectUrl: "https://my-company-api.com/add-card/callback",
},
},
{
headers: {
Authorization: `Bearer $TOKEN`,
},
}
);

return reply.json({ consentUrl });
});

The app opens an in-app browser and starts listening for deep links.

Linking.addListener("url", ({ url }) => {
// called when deep link is visited
});

// …

const sessionId = await AsyncStorage.getItem("sessionId");

const { consentUrl } = await client.post(
"https://my-company-api.com/add-card",
{ headers: { sessionId } }
);

openBrowser(consentUrl).catch((error) => {
console.error(error);
});

The user consents and is redirected to https://my-company-api.com/add-card/callback.

During redirection, Swan adds these query parameters to the URL.

  • consentId
  • env: Sandbox or Live environment
  • status: Accepted, CustomerRefused, OperationCommitting, CredentialRefused, Created, Started, Expired, Failed, or Canceled
    • Including the status doesn't replace the need to query Swan's API to confirm the consent.
  • resourceId (if the consent only impacts one resource)

Your server performs any necessary additional operations, then redirects the user to a deep link.

app.get("/add-card/callback", async (req, reply) => {
// perform additional operations…
return reply.redirect("com.company.myapp://close");
});

Finally, the listener closes the in-app browser.

Linking.addListener("url", ({ url }) => {
// called when deep link is visited

if (url.startsWith("com.company.myapp://close")) {
closeBrowser(); // required on iOS
}
});

const sessionId = await AsyncStorage.getItem("sessionId");

const { consentUrl } = await client.post(
"https://my-company-api.com/add-card",
{ headers: { sessionId } }
);

openBrowser(consentUrl).catch((error) => {
console.error(error);
});