@delmaredigital/payload-better-auth 0.6.2 → 0.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -262,8 +262,15 @@
262
262
  }
263
263
  // Get Payload collection slug from model name
264
264
  // Uses factory's getModelName which respects BetterAuthOptions.modelName config
265
+ // Fix: Better Auth's getModelName blindly appends 's' even when name already ends in 's'
266
+ // (e.g., 'jwks' becomes 'jwkss'). We normalize to prevent double-s slugs.
265
267
  const getCollection = (model)=>{
266
- return getModelName(model);
268
+ const name = getModelName(model);
269
+ // Fix double-s from naive pluralization (e.g., jwkss → jwks)
270
+ if (name.endsWith('ss') && !model.endsWith('ss')) {
271
+ return name.slice(0, -1);
272
+ }
273
+ return name;
267
274
  };
268
275
  // The CustomAdapter interface uses generics (T) for return types.
269
276
  // Payload returns concrete types (JsonObject & TypeWithID).
@@ -552,6 +552,90 @@ let apiKeyPermissionsConfig = undefined;
552
552
  headers
553
553
  });
554
554
  if (!sessionData?.user?.id) {
555
+ // No session found — check for OAuth JWT Bearer token
556
+ const authHeader = headers.get('authorization');
557
+ if (authHeader?.startsWith('Bearer ')) {
558
+ const token = authHeader.slice(7);
559
+ // Try OAuth JWT verification via the oauth-provider's verifyAccessToken
560
+ try {
561
+ const { verifyAccessToken } = await import('better-auth/oauth2');
562
+ const baseURL = auth.options?.baseURL;
563
+ if (!baseURL) throw new Error('baseURL not configured');
564
+ const jwtPayload = await verifyAccessToken(token, {
565
+ jwksUrl: `${baseURL}/api/auth/jwks`,
566
+ verifyOptions: {
567
+ issuer: baseURL,
568
+ audience: baseURL
569
+ }
570
+ });
571
+ if (jwtPayload?.sub) {
572
+ const users = await payload.find({
573
+ collection: usersCollection,
574
+ where: {
575
+ id: {
576
+ equals: jwtPayload.sub
577
+ }
578
+ },
579
+ limit: 1,
580
+ depth: 0
581
+ });
582
+ if (users.docs.length > 0) {
583
+ // Extract org context and scopes from JWT claims
584
+ const oauthOrgId = jwtPayload.organizationId;
585
+ const oauthScopes = typeof jwtPayload.scope === 'string' ? jwtPayload.scope.split(' ') : Array.isArray(jwtPayload.scope) ? jwtPayload.scope : [];
586
+ // Look up org role if orgId is present
587
+ let orgRole;
588
+ if (oauthOrgId) {
589
+ try {
590
+ const memberships = await payload.find({
591
+ collection: membersCollection,
592
+ where: {
593
+ and: [
594
+ {
595
+ user: {
596
+ equals: jwtPayload.sub
597
+ }
598
+ },
599
+ {
600
+ organization: {
601
+ equals: oauthOrgId
602
+ }
603
+ }
604
+ ]
605
+ },
606
+ limit: 1,
607
+ depth: 0
608
+ });
609
+ if (memberships.docs.length > 0) {
610
+ orgRole = memberships.docs[0].role;
611
+ }
612
+ } catch {
613
+ // Members collection might not exist
614
+ }
615
+ }
616
+ const userDoc = users.docs[0];
617
+ const oauthUser = {
618
+ ...userDoc,
619
+ id: userDoc.id,
620
+ oauthScopes,
621
+ collection: usersCollection,
622
+ _strategy: 'better-auth',
623
+ ...oauthOrgId ? {
624
+ activeOrganizationId: oauthOrgId
625
+ } : {},
626
+ ...orgRole ? {
627
+ organizationRole: orgRole
628
+ } : {}
629
+ };
630
+ return {
631
+ user: oauthUser
632
+ };
633
+ }
634
+ }
635
+ } catch {
636
+ // JWT verification failed — not a valid OAuth token, continue to return null
637
+ }
638
+ }
555
639
  return {
556
640
  user: null
557
641
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delmaredigital/payload-better-auth",
3
- "version": "0.6.2",
3
+ "version": "0.6.4",
4
4
  "description": "Better Auth adapter and plugins for Payload CMS",
5
5
  "type": "module",
6
6
  "license": "MIT",