@@ -365,6 +365,10 @@ model User {
365365 chats Chat []
366366 sharedChats ChatAccess []
367367
368+ oauthTokens OAuthToken []
369+ oauthAuthCodes OAuthAuthorizationCode []
370+ oauthRefreshTokens OAuthRefreshToken []
371+
368372 createdAt DateTime @default (now () )
369373 updatedAt DateTime @updatedAt
370374
@@ -485,3 +489,64 @@ model ChatAccess {
485489
486490 @@unique ([chatId , userId ] )
487491}
492+
493+ // OAuth2 Authorization Server models
494+ // @see: https://datatracker.ietf.org/doc/html/rfc6749
495+
496+ /// A registered OAuth2 client application (e.g. Claude Desktop, Cursor).
497+ /// Created via dynamic client registration (RFC 7591) at POST /api/ee/oauth/register.
498+ model OAuthClient {
499+ id String @id @default (cuid () )
500+ name String
501+ logoUri String ?
502+ redirectUris String []
503+ createdAt DateTime @default (now () )
504+
505+ authCodes OAuthAuthorizationCode []
506+ tokens OAuthToken []
507+ refreshTokens OAuthRefreshToken []
508+ }
509+
510+ /// A short-lived authorization code issued during the OAuth2 authorization code flow.
511+ /// Single-use and expires after 10 minutes. Stores the PKCE code challenge.
512+ model OAuthAuthorizationCode {
513+ codeHash String @id // hashSecret(rawCode)
514+ clientId String
515+ client OAuthClient @relation (fields : [clientId ] , references : [id ] , onDelete : Cascade )
516+ userId String
517+ user User @relation (fields : [userId ] , references : [id ] , onDelete : Cascade )
518+ redirectUri String
519+ codeChallenge String // BASE64URL(SHA-256(codeVerifier))
520+ resource String ? // RFC 8707: canonical URI of the target resource server
521+ expiresAt DateTime
522+ createdAt DateTime @default (now () )
523+ }
524+
525+ /// An opaque OAuth2 refresh token. Single-use with rotation (RFC 6749 Section 10.4, OAuth 2.1 Section 4.3.1).
526+ model OAuthRefreshToken {
527+ hash String @id // hashSecret(rawToken secret portion)
528+ clientId String
529+ client OAuthClient @relation (fields : [clientId ] , references : [id ] , onDelete : Cascade )
530+ userId String
531+ user User @relation (fields : [userId ] , references : [id ] , onDelete : Cascade )
532+ scope String @default (" " )
533+ resource String ? // RFC 8707
534+ expiresAt DateTime
535+ createdAt DateTime @default (now () )
536+
537+ @@index ([clientId , userId ] )
538+ }
539+
540+ /// An opaque OAuth2 access token. The raw token is never stored — only its HMAC-SHA256 hash.
541+ model OAuthToken {
542+ hash String @id // hashSecret(rawToken secret portion)
543+ clientId String
544+ client OAuthClient @relation (fields : [clientId ] , references : [id ] , onDelete : Cascade )
545+ userId String
546+ user User @relation (fields : [userId ] , references : [id ] , onDelete : Cascade )
547+ scope String @default (" " )
548+ resource String ? // RFC 8707: canonical URI of the target resource server
549+ expiresAt DateTime
550+ createdAt DateTime @default (now () )
551+ lastUsedAt DateTime ?
552+ }
0 commit comments