Skip to content

Sandcastle oauth with ion#13403

Open
jjspace wants to merge 8 commits into
mainfrom
sandcastle-oauth
Open

Sandcastle oauth with ion#13403
jjspace wants to merge 8 commits into
mainfrom
sandcastle-oauth

Conversation

@jjspace
Copy link
Copy Markdown
Contributor

@jjspace jjspace commented Apr 14, 2026

Description

Adds basic Login functionality using ion oauth. This also provides a "User" context for the whole app so other components can depend on the currently logged in user. As a proof of concept there is a new Insert > Access token option with the editor that will add your account's default access token to the current sandcastle when logged in. This will be replaced with a selection modal for multiple tokens later.

This PR replaces the #13079 PR which had a mix of changes that have already been merged.

Currently login is only enabled in local development because we need to set up additional domains and deployments to securely enable in deployed environments.

Issue number and link

Private repo issue

Testing plan

  • run npm run build-sandcastle to build with the new login UI
  • run npm start and open local sandcastle
  • Click Login in the upper right and login with oauth
  • Test importing your token into the editor
    • Clicking the Test fetch will send a request that can be checked in the network tab, this is also temporary
  • Test Logging out and back in
  • Test clicking Deny on the oauth screen
  • Navigate to a specific sandcastle and Log-in and make sure you return to that same sandcastle after the auth page.

Author checklist

  • I have submitted a Contributor License Agreement
  • I have added my name to CONTRIBUTORS.md
  • I have updated CHANGES.md with a short summary of my change
  • I have added or updated unit tests to ensure consistent code coverage
  • I have updated the inline documentation, and included code examples where relevant
  • I have performed a self-review of my code

AI acknowledgment

  • I used AI to generate content in this PR
  • If yes, I have reviewed the AI-generated content before submitting

If yes, I used the following Tools(s) and/or Service(s):

If yes, I used the following Model(s):

@github-actions
Copy link
Copy Markdown
Contributor

Thank you for the pull request, @jjspace!

✅ We can confirm we have a CLA on file for you.

@jjspace jjspace mentioned this pull request Apr 16, 2026
9 tasks
@ggetz
Copy link
Copy Markdown
Contributor

ggetz commented Apr 21, 2026

Thanks @jjspace!

  1. Could you please capture this task in a dedicated issue so we can track?

Currently login is only enabled in local development because we need to set up additional domains and deployments to securely enable in deployed environments.

  1. Could you please capture this in a TODO list in this PR with this item as a reminder to clean this up before merging?

Test importing your token into the editor
Clicking the Test fetch will send a request that can be checked in the network tab, this is also temporary

  1. I notice there is a "Logging in..." string that flashes in the navbar. Could we remove that and replace it with a skeleton or less obvious placeholder?

  2. Is there any contributor documentation we should update with links to the relevant Cesium ion docs?

@ggetz
Copy link
Copy Markdown
Contributor

ggetz commented Apr 21, 2026

image

IMO, showing this button at all if the feature is not available is a bit confusing. Can we hide if disabled?

Copy link
Copy Markdown
Contributor

@ggetz ggetz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Currently, this stops short of comprehensive error handling and communicating that status to the user. Any thoughts on how to address this?
  • Can we put the OAuth credentials in an environment variable instead of hardcoded, like in buildSandcastle.js and the cite config?

* @param {ImportList} options.imports Set of imports to add to the import map for the iframe and standalone html pages. These paths should match the URL where it can be accessed within the current environment.
* @param {string} options.outerOrigin Origin of the outer application
* @param {string} options.innerOrigin Origin of the inner viewer bucket. Defaults to the outerOrigin if not provided
* @param {{clientId: string, callbackUrl: string}} [options.ionClientSettings] Origin of the inner viewer bucket. Defaults to the outerOrigin if not provided
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the wrong parameter desciption.

response_type: "code",
scope: this.config.scopes.join(" "),
state: stateId,
}).toString()}`,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the toString call is redundant when inside of a template string.

async tokenExchange(code: string, state: UUID) {
// @see https://cesium.com/learn/ion/ion-oauth2/#step-3-token-exchange
// Retrieve the verifier based on the state
const { codeVerifier, previousPage } = (await getPkceState(state)) ?? {};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this handle any errors thrown via a try/catch?

Comment on lines +46 to +48
this.oauth =
oauth ?? new IonOAuth({ ion, ionApi, callbackUrl, clientId, scopes });
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit non-obvious, when treating this class as a black box, that the parameters are either/or. Worth some brief inline jsdocs?

}

this.initStatus = "IN_PROGRESS";
let deferredResolve;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the deferred resolve and saved initPromise property? This is an anti-pattern we've tried to avoid in other parts of CesiumJS codebase as it can lead to unhandled errors.

Can this all be handle within the Promise constructor? Or could the remainder of this function be captured in it's own async function?

await crypto.subtle.digest("SHA-256", new TextEncoder().encode(input)),
);
const base64 = result.toBase64({ alphabet: "base64url", omitPadding: true });
console.log("base64", base64);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do a pass and cleanup any rogue console.log statements.

* table for this if you can spare it, as it uses ~150 bytes per actively logging in user, and
* public abuse could easily kill your server.
*/
const MAX_AGE = 60 * 1000;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is max-age specified in the response headers from ion? Should we be using that instead to account for any upstream changes in ion?

if (!resp.username) {
return undefined;
}
// TODO: remove just testing
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

// However we should probably add the option to update it in place
return;
}
// TODO: this is an async edit of the code. Probably need a way to lock the editor?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be handled?

}

/* eslint-disable-next-line no-extend-native */
Uint8Array.prototype.toBase64 ??= function toBase64(options) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you'll need to add the polyfill before toBase64 is called in sha256. Moving it above the exported function should be suficient.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants