The default (Auth.js) auth provider uses Auth.js and a user collection to authenticate and authorize users to your TinaCMS instance.
Note: If you have not set up self-hosting yet, we recommend using the init backend
command. See the Getting started doc for more information.
To get started you will need to install the following dependencies:
yarn add next-auth tinacms-authjs
Hint: Make sure you have a database adapter setup before continuing.
We need to add the Auth Provider and a user collection to the tina/config.{ts,js}
file.
The TinaUserCollection
is a special collection that is used to store user information. This collection will be seeded from thecontent/users/index.json
file, but the file will not be updated when the users collection is updated. This prevents sensitive information such as passwords from being written to the Git repository.
import {TinaUserCollection,UsernamePasswordAuthJSProvider,} from 'tinacms-authjs/dist/tinacms'// ...export default defineConfig({// When isLocal is true, the CMS will not require authenticationauthProvider: isLocal? new LocalAuthProvider(): new UsernamePasswordAuthJSProvider(),schema: {collections: [TinaUserCollection,// ...],},})
Create a file called content/users/index.json
that contains the initial seed user:
{"users": [{"name": "Tina User","email": "user@tina.io","username": "admin","password": "admin"}]}
After logging in, you can update the username and password
Update your /api/tina/[...routes].{ts,js}
file to use the Auth.js backend.
import { TinaNodeBackend, LocalBackendAuthProvider } from '@tinacms/datalayer'import { TinaAuthJSOptions, AuthJsBackendAuthProvider } from 'tinacms-authjs'import databaseClient from '../../../tina/__generated__/databaseClient'const isLocal = process.env.TINA_PUBLIC_IS_LOCAL === 'true'const handler = TinaNodeBackend({authProvider: isLocal? LocalBackendAuthProvider(): AuthJsBackendAuthProvider({authOptions: TinaAuthJSOptions({databaseClient: databaseClient,secret: process.env.NEXTAUTH_SECRET,}),}),databaseClient,})export default (req, res) => {// Modify the request here if you need toreturn handler(req, res)}
Now that you have set up the Auth.js auth provider you can test it out.
Add the following to your script to your package.json file.
"dev:prod": "TINA_PUBLIC_IS_LOCAL=false tinacms dev -c \"next dev\"",
This will allow you to test the authentication flow locally.
yarn dev:prod
Now you can open your browser and navigate to http://localhost:3000/admin/index.html
and you should be redirected to the login page.
Once you have logged in, you will be prompted to update your password. This should be done so that the password is not stored in plain text in your repository.
To add additional users, navigate to the Users collection in the sidebar to add or edit users. See User Management for more details.
Auth.js supports many different login providers. When self-hosting, it is possible to use any of these providers with some configuration changes. The user collection will still be used for authorization, but the login flow will be handled by the login provider.
As an example, we can configure the Discord OAuth login provider.
Here we switch from the UsernamePasswordAuthJSProvider
to the DefaultAuthJSProvider
. We also redefine the user collection to remove the password field, as it is no longer needed.
import {TinaUserCollection,DefaultAuthJSProvider,} from 'tinacms-authjs/dist/tinacms'// ...export default defineConfig({// When isLocal is true, the CMS will not require authenticationauthProvider: isLocal ? new LocalAuthProvider() : new DefaultAuthJSProvider(),schema: {collections: [{...TinaUserCollection,fields: [{...TinaUserCollection.fields[0],fields: [{type: 'string',label: 'Username',name: 'username',uid: true,required: true,},{type: 'string',label: 'Name',name: 'name',},{type: 'string',label: 'Email',name: 'email',},],},],} as Collection,// ...],},})
Remove the password field from the seed data.
{"users": [{"name": "Tina Discord User","email": "user@tina.io","username": "mydiscorduser"}]}
Note that if you already have users in your database, you will need to remove the password field from them as well.
We'll need to split the backend into a separate endpoint, and share the AuthJS options between the two. So in /pages/api/tina/[...routes].{ts,js}
we'll have:
export const NextAuthOptions = TinaAuthJSOptions({databaseClient: databaseClient,debug: process.env.DEBUG === 'true',secret: process.env.NEXTAUTH_SECRET,uidProp: 'name',providers: [DiscordProvider({clientId: process.env.DISCORD_CLIENT_ID,clientSecret: process.env.DISCORD_CLIENT_SECRET,}),],})const handler = TinaNodeBackend({authProvider: isLocal? LocalBackendAuthProvider(): AuthJsBackendAuthProvider({ authOptions: NextAuthOptions }),databaseClient,})export default (req, res) => {return handler(req, res)}
Here we specify the DiscordProvider. Also, note that we have added a uidProp
to the options. This is used to tell Tina which property on the Discord auth object has
the unique identifier for the user. If you are not sure what this should be, you can enable debug mode to see the auth object.
Next, we'll need to create a separate endpoint for the backend. In /pages/api/auth/[...nextauth].{ts,js}
we'll have:
import NextAuth from 'next-auth'import { NextAuthOptions } from '../tina/[...routes]'export default (req, res) => {return NextAuth(NextAuthOptions)(req, res)}
Note that this is importing the NextAuthOptions from the other endpoint.
To configure the Discord OAuth application, you will need to create a new application in the Discord Developer Portal.
Once the application has been created, go to the OAuth2 tab and copy the Client ID and Client Secret. These will be used as environment variables. Add redirects for localhost http://localhost:3000/api/auth/callback/discord
and your production domain.
Add the following environment variables:
DISCORD_CLIENT_ID=<your discord client id>DISCORD_CLIENT_SECRET=<your discord client secret>
Run yarn dev:prod
to test login locally.
© TinaCMS 2019–2024