Skip to content

Conversation

cerebrl
Copy link
Collaborator

@cerebrl cerebrl commented Aug 11, 2025

JIRA Ticket

SDKS-4252

Description

This PR introduces local token get method with token auto-renew. This also introduces oauthThreshold that will renew a valid token if it's within X time until expiration. The default is 30 seconds. This also extracts the bulk of exchange out of the top-level client file into it's own file for better sharing.

Copy link

nx-cloud bot commented Aug 11, 2025

View your CI Pipeline Execution ↗ for commit 7cb0519

Command Status Duration Result
nx run-many -t build ✅ Succeeded <1s View ↗
nx affected -t build typecheck lint test e2e-ci ✅ Succeeded 2m 11s View ↗
nx-cloud record -- nx format:check ✅ Succeeded 1s View ↗

☁️ Nx Cloud last updated this comment at 2025-08-14 15:41:17 UTC

Copy link
Contributor

github-actions bot commented Aug 11, 2025

Deployed b3e84fb to https://ForgeRock.github.io/ping-javascript-sdk/pr-369/b3e84fb754a3b2acfaf5365c187464c1d6663b05 branch gh-pages in ForgeRock/ping-javascript-sdk

Copy link
Contributor

github-actions bot commented Aug 11, 2025

📦 Bundle Size Analysis

📦 Bundle Size Analysis

🚨 Significant Changes

🔺 @forgerock/oidc-client - 21.1 KB (+3.1 KB, +17.4%)

📊 Minor Changes

📈 @forgerock/sdk-types - 5.9 KB (+0.0 KB)
📈 @forgerock/sdk-request-middleware - 4.4 KB (+0.2 KB)

➖ No Changes

@forgerock/protect - 152.3 KB
@forgerock/davinci-client - 34.1 KB
@forgerock/sdk-utilities - 4.0 KB
@forgerock/device-client - 9.2 KB
@forgerock/iframe-manager - 2.4 KB
@forgerock/storage - 1.4 KB
@forgerock/sdk-logger - 1.6 KB
@forgerock/sdk-oidc - 3.5 KB


11 packages analyzed • Baseline from latest main build

Legend

🆕 New package
🔺 Size increased
🔻 Size decreased
➖ No change

ℹ️ How bundle sizes are calculated
  • Current Size: Total gzipped size of all files in the package's dist directory
  • Baseline: Comparison against the latest build from the main branch
  • Files included: All build outputs except source maps and TypeScript build cache
  • Exclusions: .map, .tsbuildinfo, and .d.ts.map files

🔄 Updated automatically on each push to this PR

Copy link

changeset-bot bot commented Aug 11, 2025

🦋 Changeset detected

Latest commit: 7cb0519

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 9 packages
Name Type
@forgerock/oidc-client Minor
@forgerock/davinci-client Minor
@forgerock/sdk-types Minor
@forgerock/sdk-utilities Minor
@forgerock/iframe-manager Minor
@forgerock/sdk-logger Minor
@forgerock/sdk-oidc Minor
@forgerock/sdk-request-middleware Minor
@forgerock/storage Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

store,
options: storageOptions,
}).pipe(
Micro.flatMap((tokens) =>
Copy link
Collaborator

Choose a reason for hiding this comment

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

may be slightly more readable if we don't nest this pipe inside of the outter pipe, and just keep them on one level.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Addressed

endpoint: wellknown.token_endpoint,
store,
options: storageOptions,
}).pipe(
Copy link
Collaborator

Choose a reason for hiding this comment

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

similar comment here

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think I addressed this as it was the same as the other comment.

store,
options,
}: BuildTokenExchangeµParams) {
return Micro.sync(() => createValuesµ(code, config, state, endpoint, options)).pipe(
Copy link
Collaborator

Choose a reason for hiding this comment

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

unneccessary Micro.sync I think?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I actually made a mistake in naming createValuesµ as it didn't return a Micro, but I fixed that and removed the Micro here.

Micro.flatMap((options) => validateValuesµ(options)),
Micro.flatMap((requestOptions) =>
Micro.promise(
async () => await store.dispatch(oidcApi.endpoints.exchange.initiate(requestOptions)),
Copy link
Collaborator

Choose a reason for hiding this comment

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

unneeccessary await and async

Copy link
Collaborator

@ryanbas21 ryanbas21 left a comment

Choose a reason for hiding this comment

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

Non blocking comments that are mostly semantic. Looks good

@codecov-commenter
Copy link

codecov-commenter commented Aug 13, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 57.58%. Comparing base (e393843) to head (7cb0519).
⚠️ Report is 4 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #369   +/-   ##
=======================================
  Coverage   57.58%   57.58%           
=======================================
  Files          32       32           
  Lines        2044     2044           
  Branches      320      320           
=======================================
  Hits         1177     1177           
  Misses        867      867           
Files with missing lines Coverage Δ
packages/davinci-client/src/lib/davinci.api.ts 0.41% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

),
Micro.flatMap((requestOptions) =>
Micro.promise(
async () => await store.dispatch(oidcApi.endpoints.exchange.initiate(requestOptions)),
Copy link
Collaborator

Choose a reason for hiding this comment

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

This oidcApi endpoint returns a TokenExchangeResponse type but the type returned by buildTokenExchangeµ is OauthTokens which has properties that don't exist in TokenExchangeResponse. Did you means to remove TokenExchangeResponse and replace it with OauthTokens?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch. Let me work on this.

log,
authorizeOptions,
).pipe(
Micro.flatMap((response): Micro.Micro<OauthTokens, TokenExchangeErrorResponse, never> => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we need the types here, I assume there was a reason we added them explicitly here instead of the inferred type

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is a good question, and I'll try my best to articulate it in as comment, but it may require a chat.

I like using inferred types as I've been developing, but as I was "tightening up" the code, and ensuring that what was being returned was exactly what I intended, inferred types was hindering me.

By explicitly declaring the types, I was declaring the contract, and if I strayed from it by changing some internals, I was immediately notified that I broke the contract. Does that make sense?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm totally fine with that. I just was double checking that it was with a purpose and not because we just didn't have something being inferred.

I actually think declaring types has benefits, especially at the edges of code that consumers touch.

Micro.sync(() => log.error('Error handling token exchange response', error)),
),
Micro.flatMap((data) =>
Micro.promise(async () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Micro.promise seems unneeded here, therefore making flatMap unneccessary. I think this can just be simplified to a map

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yup, good catch!

store,
options: storageOptions,
}).pipe(
Micro.flatMap((tokens) =>
Copy link
Collaborator

Choose a reason for hiding this comment

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

nitpick, but I think tap maybe more semantic. I think it should unwrap tokens still

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is a good callout. I've made the change in my local branch.

const state = store.getState();
const wellknown = wellknownSelector(wellknownUrl, state);

if (!wellknown?.end_session_endpoint) {
Copy link
Collaborator

@ancheetah ancheetah Aug 13, 2025

Choose a reason for hiding this comment

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

Unrelated to tokens.get but I didn't notice this before. Do we need to add a check for || !wellknown.ping_end_idp_session_endpoint? It seems the logout method calls one or the other depending on the server.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hmm, this is actually a good question. This check is more like a quick check that we have a wellknown response, more or less. It's not really intended to be a complete error proof check. I'll have to think on it.

@ancheetah
Copy link
Collaborator

One more unrelated to tokens.get, sorry. In the logout method should we be passing some key to storageClient.remove()? If not, then I'm not sure what exactly is being removed.

// Delete local token and return combined results
Micro.flatMap(([sessionResponse, revokeResponse]) =>
Micro.promise(async () => {
const deleteResponse = await storageClient.remove();

@cerebrl
Copy link
Collaborator Author

cerebrl commented Aug 14, 2025

@ancheetah

One more unrelated to tokens.get, sorry. In the logout method should we be passing some key to storageClient.remove()? If not, then I'm not sure what exactly is being removed.

// Delete local token and return combined results
Micro.flatMap(([sessionResponse, revokeResponse]) =>
Micro.promise(async () => {
const deleteResponse = await storageClient.remove();

No, the key is derived from the name saved within the closure of the storageClient from the initialization here: https://github.com/ForgeRock/ping-javascript-sdk/blob/main/packages/oidc-client/src/lib/client.store.ts#L59

@cerebrl cerebrl merged commit 777e771 into main Aug 14, 2025
3 checks passed
@cerebrl cerebrl deleted the oidc-localget branch August 14, 2025 19:45
@ryanbas21 ryanbas21 mentioned this pull request Aug 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants