Skip to content

feat(ccip-sdk): OIDC authentication for Canton Ledger API - #368

Open
SyedAsadKazmi wants to merge 8 commits into
mainfrom
feat/canton-oidc-auth
Open

feat(ccip-sdk): OIDC authentication for Canton Ledger API#368
SyedAsadKazmi wants to merge 8 commits into
mainfrom
feat/canton-oidc-auth

Conversation

@SyedAsadKazmi

@SyedAsadKazmi SyedAsadKazmi commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Adds OAuth 2.0 / OIDC auth for Canton, split across ccip-sdk (runtime-agnostic protocol primitives) and ccip-cli (Node-specific orchestration).

SDK — runtime-agnostic core (ccip-sdk/src/canton/authentication/):

  • oauth4webapi for OAuth2 protocol mechanics (discovery, PKCE, grants, token validation) — pure fetch/WebCrypto, no node:* imports
  • createMemoizedTokenFetcher() — token caching with concurrent fetch coalescing and expiry-based invalidation, backed by micro-memoize ({ async: true })
  • createAuthProvider() discriminated union selector (static / clientCredentials)
  • Authorization-code protocol helpers: buildAuthorizationRequest(), validateAuthorizationCallback(), exchangeAuthorizationCode(), refreshAuthorizationCodeToken()
  • AuthorizationCodeProvider — caching provider wrapping caller-supplied fetch/refresh callbacks
  • CantonConfig.jwt accepts string | (() => Promise<string>) — static token or per-request getter for refreshable tokens
  • CANTON_AUTH_ERROR error code + recovery hint
  • 36 unit tests

CLI — Node orchestration (ccip-cli/src/providers/canton/):

  • auth.ts — local node:http callback server, open/xdg-open browser launching, CANTON_CLIENT_ID/CANTON_CLIENT_SECRET env-var resolution, process-wide auth provider cache (prevents double login across sendshowRequests)
  • config.tsloadCantonConfig accepts auth; jwt optional when auth present. CLI resolves auth upfront into jwt (string or getter) before handing config to the SDK
  • wallet.ts — Ed25519 transaction signer + wallet loading
  • Error surfacing: Promise.allSettled results inspected to surface CCIPError instead of generic RPC_NOT_FOUND

@vercel

vercel Bot commented Aug 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
ccip-tools-ts Ready Ready Preview Sep 4, 2026 1:27pm UTC

Request Review

Comment thread ccip-sdk/src/canton/authentication/authorization-code.ts Fixed
@github-actions

This comment was marked as outdated.

Comment thread ccip-api-ref/docs-cli/configuration.mdx
const execution = await dest.execute({
messageId: '0x1234...abcd',
wallet: cantonWallet,
wallet: { party: 'receiver::1220...' }, // Canton wallet (party ID, no signer needed)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Does this actually work? ccip-cli/src/providers/canton.ts has removed the wallet from loadCantonWallet entirely.

Also, this would be the same party as already specified in cantonConfig?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You're right on both. The SDK example works (CantonChain.execute validates the wallet via isCantonWallet and uses wallet.party for actAs/payer), and the CLI's loadCantonWallet sources party entirely from cantonConfig.party , so it's the same value.

Fixed the example to extract cantonConfig into a variable and reuse cantonConfig.party instead of duplicating the string literal.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ah, I mixed up CLI and SDK. The SDK still accept and expects this wallet {party: 'asdf'}. Your change still looks good 👍

Comment thread ccip-cli/README.md Outdated
Comment thread ccip-sdk/src/canton/authentication/static.ts Outdated
Comment thread ccip-sdk/src/canton/authentication/types.ts Outdated
Comment thread ccip-sdk/src/canton/authentication/types.ts
friedemannf
friedemannf previously approved these changes Aug 28, 2026
@SyedAsadKazmi
SyedAsadKazmi marked this pull request as ready for review August 31, 2026 19:10
@SyedAsadKazmi
SyedAsadKazmi requested review from a team as code owners August 31, 2026 19:10

@andrevmatos andrevmatos left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Sorry, but we can't have this much node dependencies in the ccip-sdk;
I can see 2 usecases for this oauth worfklow for Canton: ccip-cli and an external provider (e.g. think of someone who manages to pack the Canton lib on their web-based or Electron-based app).

  • For ccip-cli, this whole server-spawning and browser open call should be handled on the cli side, and then whatever result of that needed is then handed over the cantonConfig
  • For (web?) clients embedding this workflow, there may be an intersection of the above which they may be interested on, but they probably would implement their own callback REST endpoint, and/or some form of redirect on their application.

Strictly speaking, the SDK should be handed only what it needs, and these previous steps are mostly utilities. But the way I see this, we may be able to extract the reusable core intersection of the above to some functions which MAY be exported from the ccip-sdk, and implement the nodeJS-specific bits in the cli, maybe make providers/canton/ a folder.

Comment thread .oxlintrc.json Outdated
"rules": {
"ccip/restricted-syntax": "error",
"import/no-nodejs-modules": ["error", { "allow": ["buffer"] }]
"import/no-nodejs-modules": ["error", { "allow": ["buffer", "node:child_process", "node:http"] }]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

No node modules on ccip-sdk, pls

@andrevmatos

Copy link
Copy Markdown
Collaborator

Concrete proposal for the split:

SDK — runtime-agnostic core (canton/authentication/):
keep types, metadata (RFC 8414 discovery), token-source (caching + PKCE via WebCrypto), client-credentials, static — all pure fetch, no node:*, no process.env. Revert the .oxlintrc.json allowances; the docusaurus webpack stubs then become unnecessary too. For auth-code, export only the protocol pieces an embedder can't safely rewrite: build-authorize-URL, callback validation (state + PKCE), code→token exchange, refresh grant. No callback server, no browser open.

CLI — Node orchestration (providers/canton/ folder):
owns everything environment-specific: local node:http callback server, open/xdg-open, CANTON_CLIENT_ID/CANTON_CLIENT_SECRET resolution, timeouts and terminal UX. It resolves auth upfront and hands the result to cantonConfig — the SDK never orchestrates a flow, it only consumes what it's given.

Wiring: CantonConfig takes jwt: string, or — if we want refresh to actually work (today fromUrl resolves once and every client holds a static string, so the refresh path is unreachable) — an injected () => Promise<string> token getter the clients call per request. Web/Electron embedders compose the SDK protocol functions with their own redirect/callback handling.

Comment thread ccip-sdk/src/canton/authentication/authorization-code.ts Fixed
Comment thread ccip-cli/src/providers/canton/auth.ts Fixed
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

CI Test Report

1585/1591 tests passed (392 suites) in 4m 50s

Summary

ℹ tests 1591
ℹ suites 392
ℹ pass 1585
ℹ fail 0
ℹ cancelled 0
ℹ skipped 6
ℹ todo 0
ℹ duration_ms 290031.211059
Coverage report
File                                   | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
---------------------------------------|---------|----------|---------|---------|-------------------
All files                              |   79.48 |    79.28 |   69.72 |   79.48 |                   
 ccip-cli/src                          |   90.42 |    59.25 |      75 |   90.42 |                   
  index.ts                             |   90.42 |    59.25 |      75 |   90.42 | ...73-174,180-185 
 ccip-cli/src/commands                 |   52.71 |    75.87 |    62.9 |   52.71 |                   
  index.ts                             |     100 |      100 |     100 |     100 |                   
  lane-latency.ts                      |   70.76 |    83.33 |   33.33 |   70.76 | ...,76-83,119-128 
  lane.ts                              |   86.98 |    68.42 |     100 |   86.98 | ...41-144,148-150 
  manual-exec.ts                       |   20.34 |      100 |       0 |   20.34 | ...39-149,151-347 
  parse.ts                             |    55.2 |      100 |       0 |    55.2 | 47-51,58-65,67-96 
  search.ts                            |   81.25 |      100 |       0 |   81.25 | 24-29             
  send.ts                              |   14.13 |      100 |       0 |   14.13 | ...09-237,239-552 
  show.ts                              |   75.68 |    63.75 |     100 |   75.68 | ...15-318,399-401 
  supported-tokens.ts                  |   17.93 |      100 |       0 |   17.93 | ...80-353,355-368 
  token.ts                             |   22.72 |      100 |       0 |   22.72 | ...3,60-67,69-132 
  types.ts                             |     100 |      100 |     100 |     100 |                   
  utils.ts                             |   88.13 |     80.1 |   96.66 |   88.13 | ...11-721,729-737 
 ccip-cli/src/commands/search          |   53.89 |    76.19 |    12.5 |   53.89 |                   
  messages.ts                          |   53.89 |    76.19 |    12.5 |   53.89 | ...36-258,260-295 
 ccip-cli/src/providers                |   53.37 |    86.45 |   21.73 |   53.37 |                   
  aptos.ts                             |   51.47 |      100 |       0 |   51.47 | ...,95-96,105-136 
  evm.ts                               |    36.3 |      100 |       0 |    36.3 | ...69-104,116-168 
  index.ts                             |   81.37 |    85.71 |    90.9 |   81.37 | ...20,290,344-392 
  solana.ts                            |   47.44 |      100 |       0 |   47.44 | ...02-103,112-137 
  sui.ts                               |    49.5 |      100 |       0 |    49.5 | ...57-258,267-301 
  ton.ts                               |   15.03 |      100 |       0 |   15.03 | 24-153            
 ccip-cli/src/providers/canton         |    85.4 |    77.89 |      68 |    85.4 |                   
  auth.ts                              |   82.41 |    73.01 |   53.33 |   82.41 | ...17-333,464-469 
  config.ts                            |   80.41 |    81.81 |     100 |   80.41 | 36-43,48-58       
  index.ts                             |     100 |      100 |     100 |     100 |                   
  wallet.ts                            |   92.93 |      100 |   85.71 |   92.93 | 172-184           
 ccip-sdk/src                          |   95.43 |    88.26 |    92.9 |   95.43 |                   
  chain.ts                             |   96.49 |    84.03 |   84.37 |   96.49 | ...2427,2460-2461 
  commits.ts                           |    90.9 |    81.25 |     100 |    90.9 | ...06,115-118,120 
  execution.ts                         |   93.13 |    88.23 |     100 |   93.13 | ...53-159,192-199 
  explorer.ts                          |     100 |      100 |     100 |     100 |                   
  extra-args.ts                        |     100 |    89.47 |     100 |     100 | 225,260,297,312   
  fetch.ts                             |   98.06 |    93.58 |   97.77 |   98.06 | ...-881,1127-1129 
  gas.ts                               |   85.14 |    86.04 |      75 |   85.14 | ...82-293,402-451 
  http-status.ts                       |     100 |      100 |     100 |     100 |                   
  index.ts                             |     100 |      100 |     100 |     100 |                   
  messages.ts                          |    79.2 |    47.16 |      40 |    79.2 | ...99-310,314-327 
  networks.ts                          |     100 |      100 |     100 |     100 |                   
  offchain.ts                          |   84.64 |    76.92 |     100 |   84.64 | ...18-227,236-237 
  requests.ts                          |    95.4 |    90.58 |     100 |    95.4 | ...54-655,680-685 
  supported-chains.ts                  |     100 |      100 |     100 |     100 |                   
  types.ts                             |     100 |      100 |     100 |     100 |                   
  utils.ts                             |    99.2 |       96 |     100 |    99.2 | ...12,217-218,220 
 ccip-sdk/src/api                      |   93.22 |    83.44 |   94.73 |   93.22 |                   
  index.ts                             |   93.22 |    83.44 |   94.73 |   93.22 | ...92-895,898-901 
 ccip-sdk/src/aptos                    |   78.76 |    78.51 |   76.19 |   78.76 |                   
  exec.ts                              |   29.31 |      100 |       0 |   29.31 | 18-58             
  hasher.ts                            |   75.89 |       80 |   66.66 |   75.89 | 17-36,50-56       
  index.ts                             |   68.84 |    80.15 |   67.44 |   68.84 | ...1072,1076-1087 
  logs.ts                              |   96.47 |    77.94 |    92.3 |   96.47 | ...1442-1444,1731 
  send.ts                              |    25.6 |      100 |       0 |    25.6 | ...2,63-80,93-125 
  token.ts                             |   23.75 |       75 |     100 |   23.75 | 35-156            
  types.ts                             |   65.97 |      100 |       0 |   65.97 | 26-33,65-89       
 ccip-sdk/src/canton                   |   45.32 |    76.16 |   31.66 |   45.32 |                   
  amount.ts                            |   92.59 |       70 |     100 |   92.59 | 22-23             
  ccv-addresses.ts                     |     100 |    91.07 |     100 |     100 | 13,30,64,67       
  defaults.ts                          |   99.28 |    90.24 |     100 |   99.28 | 93                
  events.ts                            |   45.38 |     52.5 |   23.52 |   45.38 | ...15-527,533-542 
  index.ts                             |   34.89 |    58.82 |   12.82 |   34.89 | ...2614,2617-2619 
  types.ts                             |   95.26 |      100 |      50 |   95.26 | 55-62             
  update-id.ts                         |   98.11 |    88.88 |     100 |   98.11 | 52                
 ccip-sdk/src/canton/authentication    |   92.93 |    79.31 |    92.3 |   92.93 |                   
  authorization-code.ts                |   89.83 |    84.31 |    90.9 |   89.83 | ...58-376,432-443 
  client-credentials.ts                |   91.28 |       75 |     100 |   91.28 | ...92-196,198-202 
  index.ts                             |    96.8 |       75 |     100 |    96.8 | 168-173           
  metadata.ts                          |   81.89 |    44.44 |      50 |   81.89 | ...55,73-78,93-94 
  static.ts                            |     100 |      100 |     100 |     100 |                   
  token-source.ts                      |   97.81 |    77.77 |     100 |   97.81 | 167-170           
  types.ts                             |     100 |      100 |     100 |     100 |                   
 ccip-sdk/src/canton/client            |    57.4 |    60.86 |   22.85 |    57.4 |                   
  client.ts                            |   55.62 |    59.09 |   22.85 |   55.62 | ...37-756,771-791 
  index.ts                             |     100 |      100 |     100 |     100 |                   
 ...dk/src/canton/explicit-disclosures |   74.81 |    69.35 |    55.1 |   74.81 |                   
  acs.ts                               |   87.36 |     69.1 |      90 |   87.36 | ...20,568,606-607 
  eds.ts                               |   54.34 |      100 |       0 |   54.34 | ...86-393,397-402 
 ccip-sdk/src/canton/token-metadata    |   61.37 |      100 |       0 |   61.37 |                   
  client.ts                            |   61.37 |      100 |       0 |   61.37 | 111-178,185-189   
 ...dk/src/canton/transfer-instruction |    52.4 |      100 |       0 |    52.4 |                   
  client.ts                            |    52.4 |      100 |       0 |    52.4 | 104-197,204-208   
 ccip-sdk/src/errors                   |   89.27 |    76.83 |   55.14 |   89.27 |                   
  CCIPError.ts                         |     100 |      100 |     100 |     100 |                   
  codes.ts                             |     100 |      100 |     100 |     100 |                   
  index.ts                             |     100 |      100 |     100 |     100 |                   
  pure.ts                              |     100 |       75 |     100 |     100 | 32                
  recovery.ts                          |     100 |      100 |     100 |     100 |                   
  specialized.ts                       |   86.89 |    73.36 |   52.71 |   86.89 | ...3636,3659-3668 
  utils.ts                             |   94.44 |    81.48 |     100 |   94.44 | 15,17,22,24       
 ccip-sdk/src/evm                      |   92.84 |    82.54 |    93.1 |   92.84 |                   
  const.ts                             |   98.66 |    92.85 |     100 |   98.66 | 122-123           
  errors.ts                            |   91.98 |    81.57 |     100 |   91.98 | ...85,239-242,247 
  extra-args.ts                        |    94.5 |    61.01 |     100 |    94.5 | ...11-212,328-340 
  fork.test.data.ts                    |     100 |      100 |     100 |     100 |                   
  gas.ts                               |   98.19 |    63.15 |     100 |   98.19 | 90-91,93          
  hasher.ts                            |     100 |     92.3 |     100 |     100 | 135               
  index.ts                             |   88.22 |    80.78 |   90.78 |   88.22 | ...2705,2860-2891 
  logs.ts                              |   99.16 |    92.02 |      90 |   99.16 | 69-70,75-76       
  messageCodec.ts                      |     100 |      100 |     100 |     100 |                   
  messages.ts                          |     100 |      100 |     100 |     100 |                   
  offchain.ts                          |    87.5 |    71.42 |     100 |    87.5 | 13-14             
  simulate.ts                          |     100 |    94.82 |     100 |     100 | 109-111,425       
  types.ts                             |     100 |      100 |     100 |     100 |                   
 ccip-sdk/src/evm/viem                 |   79.76 |    90.62 |   69.23 |   79.76 |                   
  client-adapter.ts                    |     100 |       90 |     100 |     100 | 48,74             
  index.ts                             |     100 |      100 |     100 |     100 |                   
  wallet-adapter.ts                    |   63.09 |     90.9 |   55.55 |   63.09 | ...91-124,131-157 
 ccip-sdk/src/hasher                   |   94.29 |    78.94 |     100 |   94.29 |                   
  common.ts                            |     100 |      100 |     100 |     100 |                   
  hasher.ts                            |     100 |    66.66 |     100 |     100 | 19                
  index.ts                             |     100 |      100 |     100 |     100 |                   
  merklemulti.ts                       |   93.43 |       78 |     100 |   93.43 | ...06-307,315-316 
 ccip-sdk/src/shared                   |   85.32 |    84.33 |   82.35 |   85.32 |                   
  bcs-codecs.ts                        |   79.07 |    60.86 |   66.66 |   79.07 | ...42-252,260-269 
  codec.ts                             |    94.3 |    93.22 |     100 |    94.3 | 139-140,178-186   
  constants.ts                         |     100 |      100 |     100 |     100 |                   
 ccip-sdk/src/solana                   |   77.43 |    73.07 |   82.56 |   77.43 |                   
  cleanup.ts                           |   26.95 |    66.66 |   33.33 |   26.95 | ...59-101,114-227 
  exec.ts                              |   69.11 |    62.96 |   66.66 |   69.11 | ...69-473,513-514 
  extra-args.ts                        |   59.49 |    76.47 |      60 |   59.49 | 86-122,132-190    
  fork.test.data.ts                    |     100 |      100 |     100 |     100 |                   
  gas.ts                               |   92.06 |    68.57 |     100 |   92.06 | ...,81-90,170-171 
  hasher.ts                            |   96.49 |    81.81 |     100 |   96.49 | 64-67             
  index.ts                             |   81.06 |     78.7 |   85.18 |   81.06 | ...2161,2165-2198 
  logs.ts                              |   87.79 |    74.07 |     100 |   87.79 | ...50-151,163-164 
  offchain.ts                          |     100 |      100 |     100 |     100 |                   
  patchBorsh.ts                        |   78.31 |       50 |     100 |   78.31 | ...47,65-66,72-78 
  send.ts                              |    77.6 |    33.33 |      80 |    77.6 | ...52-360,403-442 
  signatures-cache.ts                  |   79.78 |    71.15 |    87.5 |   79.78 | ...51-260,274-275 
  types.ts                             |     100 |      100 |     100 |     100 |                   
  utils.ts                             |   77.44 |    64.17 |   81.25 |   77.44 | ...54-556,588-603 
 ccip-sdk/src/sui                      |   73.18 |    70.66 |   81.14 |   73.18 |                   
  discovery.ts                         |   47.37 |    48.27 |      60 |   47.37 | ...34-782,785-819 
  events.ts                            |   89.83 |    72.67 |     100 |   89.83 | ...19-626,685-698 
  exec.ts                              |   30.71 |      100 |       0 |   30.71 | 37-89,101-153     
  hasher.ts                            |   98.19 |    66.66 |     100 |   98.19 | 35,51             
  index.ts                             |   76.12 |    70.47 |   79.68 |   76.12 | ...2314,2330-2331 
  logs.ts                              |   97.89 |    74.02 |     100 |   97.89 | ...62-165,186-187 
  objects.ts                           |    54.6 |    83.33 |   83.33 |    54.6 | ...57-313,324-467 
  types.ts                             |     100 |      100 |     100 |     100 |                   
 ccip-sdk/src/sui/manuallyExec         |   53.36 |       75 |      50 |   53.36 |                   
  encoder.ts                           |    83.9 |    66.66 |     100 |    83.9 | 51-58,73-77,82    
  index.ts                             |   33.82 |      100 |       0 |   33.82 | 47-136            
 ccip-sdk/src/ton                      |      86 |    80.53 |   88.28 |      86 |                   
  exec.ts                              |     100 |      100 |     100 |     100 |                   
  extra-args.ts                        |   98.66 |    72.72 |     100 |   98.66 | 156-157,222       
  hasher.ts                            |   78.07 |    77.77 |      75 |   78.07 | 100-108,156-187   
  index.ts                             |   84.81 |     79.6 |   79.03 |   84.81 | ...2111,2118-2119 
  logs.ts                              |   93.84 |    83.43 |     100 |   93.84 | ...09-616,672-675 
  send.ts                              |   95.52 |    66.66 |     100 |   95.52 | 37-44,188         
  ton-cache.ts                         |     100 |    96.55 |     100 |     100 | 102               
  types.ts                             |   91.24 |    81.25 |     100 |   91.24 | ...61-63,71-74,92 
  utils.ts                             |    63.1 |    77.77 |    90.9 |    63.1 | ...37-395,397-400 
 scripts                               |   87.95 |    63.88 |     100 |   87.95 |                   
  test-endpoints.ts                    |   97.98 |    72.72 |     100 |   97.98 | 137-139           
  useResource.ts                       |   83.57 |    62.29 |     100 |   83.57 | ...81-286,301-302 
---------------------------------------|---------|----------|---------|---------|-------------------

Comment thread ccip-sdk/src/canton/transfer-instruction/client.ts Outdated
Comment on lines +118 to +129
/**
* A {@link TokenSource} that caches a token and lazily re-fetches via a
* caller-supplied `fetcher` when the cached token is expired or missing.
*
* The first `token()` call fetches; subsequent calls return the cached value
* until it expires, at which point a new fetch is triggered. Concurrent callers
* share a single in-flight fetch promise to avoid duplicate token requests.
*/
export class CachingTokenSource implements TokenSource {
private current: AccessToken | undefined
private readonly fetcher: () => Promise<AccessToken>
private inFlight: Promise<AccessToken> | undefined

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think this whole TokenSource thing can be replaced by a simple memoize call, with { async: true, expires: ... }; it already handles promises, returning the single inflight promise ootb, and removing the "cache" if promise rejects. You can also simply provide it a () => string in the static case

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants