Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(provider): add Frontegg provider #11342

Merged
merged 8 commits into from
Dec 22, 2024

Conversation

vladFrontegg
Copy link
Contributor

@vladFrontegg vladFrontegg commented Jul 6, 2024

☕️ Reasoning

Add Frontegg auth provider

🧢 Checklist

  • Documentation
  • Tests
  • Ready to be merged

🎫 Affected issues

📌 Resources

Copy link

vercel bot commented Jul 6, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
auth-docs ❌ Failed (Inspect) Dec 22, 2024 1:45am
proxy ❌ Failed (Inspect) Dec 22, 2024 1:45am
1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
next-auth-docs ⬜️ Ignored (Inspect) Visit Preview Dec 22, 2024 1:45am

Copy link

vercel bot commented Jul 6, 2024

@vladFrontegg is attempting to deploy a commit to the authjs Team on Vercel.

A member of the Team first needs to authorize it.

@vladFrontegg vladFrontegg marked this pull request as ready for review July 10, 2024 18:41
@eburnette
Copy link

Watching this one so we can use Frontegg with next-auth.

@ar-to
Copy link

ar-to commented Dec 8, 2024

So did attempt to test this for existing app of my running

    "next": "14.2.5",
    "next-auth": "^4.24.11",
    "react": "18.3.1",

but ultimately got stuck on a callback redirect complaining the app id is not matching. Confused because everything does seem to match between my env vars and frontegg. Anyways here are some steps I took and notes on possibly next steps. Would be nice to get this working so it can fully work with nextauth.

  • testing on an existing project running nextauth 4.24.11 (latest).
  • the code from this PR seems to point to the new v5 beta which I’m hesitant to upgrade as is a huge lift. Though I will try to test on a fresh nextjs project to confirm.
  • the nextauth code is a bit more complicated in my existing project so its possible something is afoot but doesn't feel like it.
  • I hooked up pretty much everything up correctly, create 2 apps inside Frontegg, hooked up one to my env vars which are read into my custom nextauth provider and technically everything does seem to work up to the redirect back after I successfully auth with my user in frontegg. I get http://localhost:3000/en/login?callbackUrl=http%3A%2F%2Flocalhost%3A3000%2Fen%2Flogin&error=OAuthCallback and that screenshot error with the expect and got values being 2 different apps. The confusing part is that the expected is correct but the “got” shows the id taken from https://portal.frontegg.com/development/settings/general which at first I assumed it was the single app as per this doc https://docs.frontegg.com/docs/configure-frontegg-as-oidc-idp on setting up OCID.
  • I’m stuck here and I feel really close. My hope is that is something simple somewhere.
Screenshot 2024-12-07 at 9 43 25 PM
  • I made minor changes to the custom provider code which I think may have to do with new vs old cold between v4 and v5 but its kind of unclear to me as I'm not that familiar with nextauth internals but thats what I got from some investigation. Below is my files and changes made to try and reproduce inside an existing app.
/**
 * src/libs/FrontEggNextAuthProvider.ts
 * @module providers/frontegg
 */

import type { OAuthUserConfig, OAuthConfig } from 'next-auth/providers/oauth'

/** The returned user profile from Frontegg when using the profile callback. [Reference](https://docs.frontegg.com/docs/admin-portal-profile). */
// export interface FronteggProfile {
export interface FronteggProfile extends Record<string, any> {
  
  /** The user's unique Frontegg ID */
  sub: string

  /** The user's name */
  name: string

  /** The user's email */
  email: string

  /** A boolean indicating if the user's email is verified */
  email_verified: boolean

  /** The user's picture */
  profilePictureUrl: string

  /** The user's roles */
  roles: string[]

  /** The user's custom attributes */
  [claim: string]: unknown
}

export default function Frontegg<P extends FronteggProfile>(options: OAuthUserConfig<P>): OAuthConfig<P> {
  return {
    id: 'frontegg',
    name: 'Frontegg',
    type: 'oauth',
    authorization: `${options.issuer}/oauth/authorize`,
    token: `${options.issuer}/oauth/token`,
    userinfo: `${options.issuer}/identity/resources/users/v2/me`,
    wellKnown: `${options.issuer}/oauth/.well-known/openid-configuration`,
    issuer: options.issuer,
    profile(profile, tokens) {
      console.log('profile>>>', profile, tokens)

      return {
        id: profile.sub,
        name: profile.name,
        email: profile.email,
        image: profile.profilePictureUrl
      }
    },
    client: {
      token_endpoint_auth_method: 'client_secret_post'
    },
    options
  }
}

inside nextauth provider object

// src/libs/auth.ts

// Third-party Imports
import { PrismaAdapter } from '@auth/prisma-adapter'
import { PrismaClient } from '@prisma/client'
import type { NextAuthOptions } from 'next-auth'
import type { Adapter } from 'next-auth/adapters'

import Frontegg from './FrontEggNextAuthProvider'

const prisma = new PrismaClient()

export const authOptions: NextAuthOptions = {
  adapter: PrismaAdapter(prisma) as Adapter,

  // ** Configure one or more authentication providers
  // ** Please refer to https://next-auth.js.org/configuration/options#providers for more `providers` options
  providers: [
    // ** ...add more providers here
    Frontegg({
      clientId: process.env.AUTH_FRONTEGG_ID as string,
      clientSecret: process.env.AUTH_FRONTEGG_SECRET as string,
      issuer: process.env.AUTH_FRONTEGG_ISSUER
    })
  ],
  debug: true,

  // ** Please refer to https://next-auth.js.org/configuration/options#session for more `session` options
  session: {
    /*
     * Choose how you want to save the user session.
     * The default is `jwt`, an encrypted JWT (JWE) stored in the session cookie.
     * If you use an `adapter` however, NextAuth default it to `database` instead.
     * You can still force a JWT session by explicitly defining `jwt`.
     * When using `database`, the session cookie will only contain a `sessionToken` value,
     * which is used to look up the session in the database.
     * If you use a custom credentials provider, user accounts will not be persisted in a database by NextAuth.js (even if one is configured).
     * The option to use JSON Web Tokens for session tokens must be enabled to use a custom credentials provider.
     */
    strategy: 'jwt',

    // ** Seconds - How long until an idle session expires and is no longer valid
    maxAge: 30 * 24 * 60 * 60 // ** 30 days
  },

  // ** Please refer to https://next-auth.js.org/configuration/options#pages for more `pages` options
  pages: {
    signIn: '/login'
  },

  // ** Please refer to https://next-auth.js.org/configuration/options#callbacks for more `callbacks` options
  callbacks: {
    /*
     * While using `jwt` as a strategy, `jwt()` callback will be called before
     * the `session()` callback. So we have to add custom parameters in `token`
     * via `jwt()` callback to make them accessible in the `session()` callback
     */
    async jwt({ token, user }) {
      console.log('jwt', token)

      if (user) {
        /*
         * For adding custom parameters to user in session, we first need to add those parameters
         * in token which then will be available in the `session()` callback
         */
        token.name = user.name
      }

      return token
    },
    async session({ session, token }) {
      console.log('sessionx', session)

      if (session.user) {
        // ** Add custom params to user in session which are added in `jwt()` callback via `token` parameter
        session.user.name = token.name
      }

      return session
    }
  }
}

inside login page

...
            <Button
              color='secondary'
              className='self-center text-textPrimary'
              startIcon={<img src='/images/logos/google.png' alt='Google' width={22} />}
              sx={{ '& .MuiButton-startIcon': { marginInlineEnd: 3 } }}
              onClick={() => signIn('frontegg')}
            >
              Sign in with FrontEgg
            </Button>
...

Like I said, it actually works well up to the very end where I expected to log me in since frontegg login box works as expected. Did test it in a sample nextjs app running below directly fine but I wanted to hook it up via nextauth for good reason.

    "@frontegg/nextjs": "^9.2.0",
    "next": "^15.0.3",
    "react": "^18.3.1",

Anyways, if it finds a frontegg dev that would be great or anyone thats successfully got it working in v4. Thanks

@akdombrowski
Copy link

Thanks for providing detailed info, @ar-to, I'll see if I can reproduce or ask someone from Eng to test.
@vladFrontegg

@ar-to
Copy link

ar-to commented Dec 9, 2024

Heads up I managed to get it working after some back and forth in the frontegg slack thread here. I'll try to make some time to create a sample repo for reference in case it helps. In the mean time here are the notes I got from that, which I also shared in the slack thread.

  • support noted that the frontegg-nextjs library does not support mutiple apps but this approach does not use that lib but just a custom nextauth provider per this PR and my comment above.
  • from the support comments in that thread, I ended up deleting all apps, setting the default app ("sample app" created upon onboarding) to auto-setting and as default so all users get assigned to it.
  • I updated my env vars to point to the main credentials found at https://portal.frontegg.com/development/settings/general "keys/domains". Whats confusing is that the docs point to "environments" but not this section so its not clear.
  • seems like the default app is the key BUT I'm still testing for my scenario but seems like may not work for third party apps since I don't see now docs on using OAuth2 with this. Would be great but this could be a feature thats not yet available. Anyways hope it helps.

@ar-to
Copy link

ar-to commented Dec 10, 2024

One final update I think. Went ahead and added a demo app that implements the above custom provider successfully. This will get replaced once this PR is merged but it would definitely be helpful for us to have a demo showing the most updated version working to help devs out. An article would be bonus too. I'll try make time for an article but just heads up to the frontegg team as that would speed up adoption and are closer to this! Hope this all helps. https://github.com/ar-to/frontegg-nextjs-samples/tree/master/demos/nextjs15-nextauth4

@akdombrowski
Copy link

Thanks for following up and the further detail, @ar-to! An article of some kind is definitely on the list to do. There was a recent UI update, so some of the instructions in there need to be updated as well on where to find various values.

seems like the default app is the key BUT I'm still testing for my scenario but seems like may not work for third party apps since I don't see now docs on using OAuth2 with this.

You may want to look under Authnetication > Social logins if you're connecting to something like Google/Apple/GitHub or under Authentication > SSO in the admin console for setting up a more general OIDC (OAuth) connection.

@ar-to
Copy link

ar-to commented Dec 11, 2024

seems like the default app is the key BUT I'm still testing for my scenario but seems like may not work for third party apps since I don't see now docs on using OAuth2 with this.

You may want to look under Authnetication > Social logins if you're connecting to something like Google/Apple/GitHub or under Authentication > SSO in the admin console for setting up a more general OIDC (OAuth) connection.

@akdombrowski actually i think what I'm looking to do is via SAML. I added the resources I found for this inside a new demo need @ https://github.com/ar-to/frontegg-nextjs-samples/tree/master/demos/nextjs-saml for future use hopefully. I posted an thorough feature request on slack so please take a look and let me know what you think.

Also, I did manage to test this SSO setup in production with a domain rather than just localhost:3000 and its working. Something interesting I found is that if I have that live site up in a new Chrome Incognito browser and then run a localhost site, it will allow me to log in to both. I just have to press login and it works. I like the UX but not positive that expected.

@ar-to
Copy link

ar-to commented Dec 11, 2024

added a demo for implementing this custom provider into a nextjs15+authjs(nextauth5 beta) app @ https://github.com/ar-to/frontegg-nextjs-samples/tree/master/demos/nextjs15-authjs. Works as expected.

Copy link
Member

@ndom91 ndom91 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exemplary new provider PR! Haha

Thanks, LGTM 👍

@ndom91 ndom91 changed the title Add Frontegg provider feat(provider): add Frontegg provider Dec 22, 2024
@ndom91 ndom91 merged commit 517c877 into nextauthjs:main Dec 22, 2024
5 of 7 checks passed
@Mideee419 Mideee419 mentioned this pull request Jan 3, 2025
3 tasks
falcowinkler pushed a commit to halvaradop/next-auth that referenced this pull request Jan 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core Refers to `@auth/core` providers
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants