Skip to content

Deep linking URLs missing base path in Firebase Cloud Functions (serverless deployment) #267

@StefanMedverse

Description

@StefanMedverse

Description
When deploying ltijs in Firebase Cloud Functions, the deep linking flow generates URLs that are missing the Cloud Function's base path prefix, resulting in 404 errors.
Environment

ltijs version: 5.9.7
ltijs-firestore version: 2.2.0
Deployment: Firebase Cloud Functions (Node.js 22)
Framework: Express (via ltijs)

The Problem
In Firebase Cloud Functions, all routes are automatically prefixed with the function name. For example, if the function is named ltiFunction, all routes become:

/ltiFunction/login
/ltiFunction/deeplink
/ltiFunction/launch

However, when ltijs generates URLs during the deep linking flow (specifically after lti.DeepLinking.createDeepLinkingMessage()), it creates URLs without this prefix:

https://domain.com/deeplink?ltik=... ❌
Instead of: https://domain.com/ltiFunction/deeplink?ltik=... ✓

This causes the LMS to redirect users to a non-existent route, resulting in 404 errors.

Configuration

await lti.setup(
    encryptionKey,
    { plugin: firestoreAdapter },
    {
        appRoute: "/launch",
        loginRoute: "/login",
        keysetRoute: "/keys",
        deeplinkingRoute: "/deeplink",
        cookies: {
            secure: true,
            sameSite: "None"
        },
        sessionTimeout: 3600,
        serverless: true,
        https: false,
        devMode: false
    }
);

await lti.deploy({ serverless: true });
The Cloud Function is deployed as:
javascriptexport const ltiFunction = onRequest(httpsOpts, async (req, res) => {
    await initOnce();
    return lti.app(req, res);
});

Expected Behavior
ltijs should generate URLs that include the base path where the Express app is mounted. When deployed as a Cloud Function named ltiFunction, the generated URLs should be:

https://domain.com/ltiFunction/deeplink?ltik=...
https://domain.com/ltiFunction/launch

Actual Behavior
ltijs generates URLs without the function name prefix:

https://domain.com/deeplink?ltik=...
https://domain.com/launch

Attempted Workarounds

Setting Express mountpath: Tried setting lti.app.mountpath = '/ltiFunction' but ltijs doesn't seem to use this property when building URLs.
URL rewriting: Currently using regex to fix URLs in the generated HTML:

const fixedForm = form.replace(
    /https:\/\/([^\/]+)\/deeplink/g,
    'https://$1/ltiFunction/deeplink'
);

This works but is not an ideal solution.

Separate redirect function: Tried creating a separate Cloud Function to redirect /deeplink → /ltiFunction/deeplink, but this loses session state.

Question
Is there a proper way to configure ltijs to be aware of the base path when deployed in a serverless environment where routes are prefixed? For example:

A configuration option to set the base URL or path prefix?
A way to tell ltijs about the Express app's mount point?
Any other recommended approach for Cloud Functions deployment?

Additional Context
This issue is specific to serverless platforms that automatically prefix routes:

Firebase Cloud Functions (function name prefix)
AWS Lambda with API Gateway (stage prefix)
Azure Functions (function app prefix)

A configuration option to set a base path would make ltijs more compatible with these serverless environments.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions