fix: OIDC token exchange, client secret hashing, and userinfo handle resolution
Three bugs discovered during Audiobookshelf OIDC integration: 1. authorize.ts: Pass explicit redirect_uri to AT Protocol OAuth callback handler. The @atproto/oauth-client library falls back to clientMetadata.redirect_uris[0] (/auth/callback) when no redirect_uri is provided, but the authorization request used /oauth/callback. 2. token.ts, revoke.ts: Hash incoming client secret with SHA-256 before comparing against stored hash. The dashboard stores hashed secrets but the token/revoke endpoints were comparing raw secrets against hashes. 3. userinfo.ts: Resolve DID to handle via AT Protocol API (app.bsky.actor.getProfile) when no cached mapping exists. Previously returned empty handle, causing clients to reject the callback.