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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions