I’m building an ICP app with Google OAuth and having an issue with principals changing on every login.
When I login with the same Google account, logout, then login again, I get a completely different principal each time. This causes all user data to be lost since it’s tied to the principal.
My approach:
- User logs in with Google OAuth, I get their JWT token with a sub field (Google’s unique user ID)
- Backend extracts the sub from JWT and derives a deterministic principal using SHA224(DER_encoded_pubkey) + 0x02 suffix
- Backend creates a DER-encoded Ed25519 public key from SHA256(sub + origin)
- Backend returns both the deterministic principal and the public key to frontend
- Frontend creates a delegation chain with this deterministic public key
- Frontend calls DelegationIdentity.fromDelegation(randomSessionKey, delegationChain)
The backend consistently returns the same principal for the same user, but the frontend identity.getPrincipal() gives different values each login.
However, when I call identity.getPrincipal(), it returns a different principal every time I login, even though the backend is giving me the same deterministic public key.
My question is: does DelegationIdentity.fromDelegation() derive the principal from the random session key or from the public key in the delegation chain? If it uses the session key, how can I make it use the deterministic public key instead?
For now I’m working around this by using email as the primary identifier instead of principals, but I’d like to understand why the deterministic principal approach isn’t working.
Environment: Internet Computer, Rust backend with ic-cdk, React frontend with @dfinity/agent and @dfinity/identity, Google OAuth 2.0
Relevant code:
- Backend: src/backend/src/lib.rs (prepare_delegation and get_delegation functions, derive_user_principal and derive_user_pubkey functions)
- Frontend: src/frontend/utils/oauthDelegation.ts (loginWithOAuth function)
Full code available at: GitHub - aliscie2/weeekaly.com