Skip to content

Mashlib OIDC login requires solid-logic workaround for token exchange #13

Description

@melvincarvalho

Summary

Mashlib OIDC login does not complete without a workaround patch to solid-logic. The upstream solid-client-authn-browser library receives the OAuth authorization code but fails to exchange it for tokens.

Root Cause Analysis

The AuthCodeRedirectHandler in solid-client-authn-browser looks up session data using the OAuth state parameter:

// AuthCodeRedirectHandler.ts:91-97
const storedSessionId = await this.storageUtility.getForUser(
  oauthState,  // Uses STATE parameter as lookup key
  "sessionId",
  { errorIfNull: true }  // Throws if not found!
);

This expects localStorage to have:

  • solidClientAuthenticationUser:{state}{sessionId: "default"}

But we observed only:

  • solidClientAuthenticationUser:default{codeVerifier, clientId, ...}

Hypothesis: The state→sessionId mapping isn't being created during login, so the redirect handler can't find the session data.

Observable Behavior

  1. User clicks "Sign In" → redirects to IdP
  2. User logs in → redirects back with ?code=xxx&state=yyy
  3. handleIncomingRedirect() is called
  4. No POST to /idp/token - token exchange never happens
  5. session.info.isLoggedIn remains false

localStorage State After Redirect

{
  "solidClientAuthenticationUser:default": {
    "clientId": "client_xxx",
    "codeVerifier": "abc123...",
    "redirectUrl": "http://jss:4000/",
    "issuer": "http://jss:4000/"
  }
}

Missing: solidClientAuthenticationUser:{state} entry.

Workaround Implementation

Patch solid-logic/src/authn/SolidAuthnLogic.ts:

1. Capture auth code at module load (before library cleans URL)

let capturedAuthCode: { code: string, state: string } | null = null
;(function() {
  const url = new URL(window.location.href)
  const code = url.searchParams.get('code')
  const state = url.searchParams.get('state')
  if (code && state) capturedAuthCode = { code, state }
})()

2. Manual token exchange in checkUser()

// Find session data (stored under sessionId, not state)
for (key of localStorage starting with 'solidClientAuthenticationUser:') {
  if (data.codeVerifier && data.clientId) { sessionData = data; break }
}

// POST to token endpoint manually
const response = await fetch(issuer + '/idp/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type: 'authorization_code',
    code: capturedAuthCode.code,
    redirect_uri: sessionData.redirectUrl,
    client_id: sessionData.clientId,
    code_verifier: sessionData.codeVerifier
  })
})

3. Patch window.fetch for authenticated requests

// rdflib uses window.fetch directly, not session.fetch
const originalFetch = window.fetch
window.fetch = (input, init) => {
  if (tokens && url.startsWith(window.location.origin)) {
    init.headers.set('Authorization', 'Bearer ' + tokens.accessToken)
  }
  return originalFetch(input, init)
}

Environment

  • solid-client-authn-browser: 3.1.1
  • solid-logic: 4.0.1
  • Browser: Chrome 131
  • Server: JSS with oidc-provider

Questions

  1. Is this specific to JSS's oidc-provider, or does it affect other servers?
  2. Is the state→sessionId mapping supposed to be stored by oidc-client-ext?
  3. Has this worked historically and regressed, or is it a configuration issue?

Related

Status

  • Workaround implemented and tested
  • Root cause identified (state→sessionId mapping missing)
  • Determine if JSS-specific or general issue
  • Upstream fix pending

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions