Prisma1: Prisma deploy broke my API with "Your token is invalid"

Created on 3 May 2019  路  20Comments  路  Source: prisma/prisma1

Describe the bug

While working on my API, I added the @unique directive to a field.

Then I ran prisma deploy (note that adding or not @unique would not resolve the bug).

And now, everytime I try to make a request to my own API (not prisma, my API that is using nexus and nexus-prisma), I get a 3105 error:

{
  "data": {
    "client": null
  },
  "errors": [
    {
      "message": "Your token is invalid. It might have expired or you might be using a token from a different project.",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "client"
      ],
      "code": 3015,
      "requestId": "local:cjv88sj9q000c0792jxgwkq22"
    }
  ]
}

But I've never implemented a jwt authentication method on my API, so it has to do with what prisma generated.

To Reproduce
Actually, I really don't know because I've never had this problem before and it has been weeks since I've run prisma deploy.

I get back on it today and impossible to make it work.

I tried to upgrade prisma CLI, downgrade it, upgrade my node modules, downgrade them, upgrade prisma, downgrade it... :cry: still that same issue of authentication.

Expected behavior
Just to work as before: return the data from the database without an authentication token (which is neither the prisma service token, neither the prisma cluster token).

Versions (please complete the following information):

  • Connector: Postgres
  • Prisma Server: 1.31 & 1.32
  • prisma CLI: prisma/1.32.2 node-v10.15-alpine
  • OS: Ubuntu 18.04.2
  • other dependencies:
    "graphql": "^14.2.1", "graphql-yoga": "^1.17.4", "nexus": "^0.11.6", "nexus-prisma": "^0.3.5", "prisma-client-lib": "^1.31.1"

Additional context
I run both prisma and my API in docker containers.
Don't know if it can be related (don't guess so because it used to work as a charm before this bug).

kinquestion areclient

Most helpful comment

Heads up for folks finding this from google:

You need to load your dotenv before importing prisma

GOOD:

import { config } from "dotenv";
config();
import { prisma } from "./generated/prisma-client";

BAD:

import { prisma } from "./generated/prisma-client";
import { config } from "dotenv";
config();

Why?

Prisma's generated client runs a constructor that grabs your env variables in the root scope, so when you import, prisma will look at your env variables. And since you haven't loaded them in yet, prisma won't be able to generate a proper token for you.

export const Prisma = makePrismaClientClass<ClientConstructor<Prisma>>({
  typeDefs,
  models,
  endpoint: `https://your-endpoint.com`,
  secret: `${process.env["YOUR_SECRET"]}`
});

All 20 comments

I've noticed something (maybe not related), but I get different results whether I run prisma generate or prisma deploy.

With prisma deploy:
generated/prisma-client/index.ts

// Code generated by Prisma ([email protected]). DO NOT EDIT.

With prisma generate:
generated/prisma-client/index.ts

// Code generated by Prisma ([email protected]). DO NOT EDIT.

Obviously that's not the only diff between the two files, but if I paste it here, it would take hundreds of lines.

@nagman

Looks like you have set a secret property in your prisma.yml. Please pass that to prisma client as well so that I can authenticate with the API.

@pantharshit00

What do you mean by passing the secret to prisma client?
Doesn't prisma already handle this, according to https://www.prisma.io/docs/prisma-server/authentication-and-security-kke4/#prisma-services?

Also note that prisma generate automatically injects the secret into the generated Prisma client. So there's no need to generate a service token explicitly.

And did you mean "so that it can authenticate with the API"?

No, you will require pass the secret to the Prisma client, example: https://github.com/javascript-af/javascript-af/blob/9ab57f97da05d672f429919b35c3a163ca337b3e/packages/backend/src/apolloServer.ts#L8

Prisma.yml secret property just tells the Prisma server to protect that service with the given secret.

"It" is Prisma client

It's already done by the prisma generator.
Here's the end of generated/prisma-client/index.ts:

export const Prisma = makePrismaClientClass<ClientConstructor<Prisma>>({
  typeDefs,
  models,
  endpoint: `http://prisma:4466`,
  secret: `${process.env["PRISMA_SECRET"]}`
});
export const prisma = new Prisma();

OMG! I solved it thanks to your pointing :smiley:

The fact was that PRISMA_SECRET was not provided as an environment variable to my API in docker-compose.yml.

I added it:

version: '3.4'
services:
  api:
    environment:
      - PRISMA_SECRET=${PRISMA_SECRET}

And it works :tada:

鈿狅笍 Please Reopen

Having a similar issue; tried this solution and the many others on the Prisma forum and no luck.

I have my PRISMA_SECRET configured in a .env file. I can use my base Prisma endpoint/admin dashboard without a problem if I generate a token using prisma token and add it to query headers.

The problem arises when I try to query from an API generated by nexus. Like @nagman, my secret token is present in generated/prisma-client/index.ts, but when I add this token as generated from the CLI to my API, I get the Your token is invalid. error.

I've tried everything from resetting cache, to changing databases, to adding/removing management and secret keys, to changing ports, to generating my own secret token using JWT, to trying ApolloServer instead of GraphQLServer from graphql-yoga; the list goes on and on.

To make matters even worse, I get the same Your token is invalid. even when I don't have any secret set at all.

Really looking for any possible help I can get; I've already spent a good 6 hours trying to fix this issue.

To make matters even worse, I get the same Your token is invalid. even when I don't have any secret set at all.

That's very odd, I've never had this issue without the secret keys. Would you be able to reproduce that bug from scratch?

Do your generated/prisma-client/index.ts look like this?

export const Prisma = makePrismaClientClass<ClientConstructor<Prisma>>({
  typeDefs,
  models,
  endpoint: `http://prisma:4466`,
  secret: `${process.env["PRISMA_SECRET"]}`
});
export const prisma = new Prisma();

Yep, my generated Prisma constructor looks exactly like this, and I have a PRISMA_SECRET set as an environment variable within the project.

I've been able to replicate this issue with the following procedure:

  1. prisma init
  2. > Create New Database (PostgresQL)
  3. > Prisma TypeScript Client
  4. Copy + paste issue-free schema into datamodel.prisma.
  5. yarn add nexus graphql nexus-prisma prisma-client-lib graphql-yoga
  6. yarn add typescript ts-node-dev --dev
  7. Update post-deploy hooks in prisma.yml:
hooks:
  post-deploy:
    - npx nexus-prisma-generate --client ./generated/prisma-client --output ./generated/nexus-prisma
  1. Add tsconfig.json.
{
  "compilerOptions": {
    "sourceMap": true,
    "outDir": "dist",
    "strict": true,
    "lib": ["esnext", "dom"]
  }
}
  1. Run npm init in the project and add the "start script": "start": "ts-node-dev --no-notify --respawn --transpileOnly ./"
  2. Create Docker volumes with docker-compose up -d.
  3. Deploy Prisma server with prisma deploy.
  4. Paste the following server code into index.ts.
import { prisma } from './generated/prisma-client'
import datamodelInfo from './generated/nexus-prisma'
import * as path from 'path'
import { prismaObjectType, makePrismaSchema, } from 'nexus-prisma'
import { GraphQLServer } from 'graphql-yoga'

const Query = prismaObjectType({
  name: 'Query',
  definition(t) {
    t.prismaFields(['*'])
  }
})

const Mutation = prismaObjectType({
  name: 'Mutation',
  definition(t:any) {
    t.prismaFields(['*'])
  }
})

const schema = makePrismaSchema({
    types: [ Query, Mutation ],
    prisma: {
        datamodelInfo,
        client: prisma
    },
    outputs: {
        schema: path.join(__dirname, './generated/schema.graphql'),
        typegen: path.join(__dirname, './generated/nexus.ts'),
    },
})

const server = new GraphQLServer({
    schema,
    context: (req) => ({
      ...req,
      prisma
    })
})

server.start(({ port }) => console.log(`Server is running on http://localhost:${port}`))
  1. Run the API with yarn start.
  2. At this point, basic CRUD resolvers and queries work perfectly fine without Authorization. Now, I add a few custom resolvers into /src/ and import them into index.ts as follows:
...
import Mutations from './src'
...
const schema = makePrismaSchema({
    types: [ Query, Mutation, ...Mutations ],
...
  1. I also create a .env file in the root of the project that looks as follows:
...
PRISMA_SECRET=someprismasecret
...
  1. Add secret: ${env:PRISMA_SECRET} below datamodel in prisma.yml.
  2. Add PRISMA_SECRET: ${PRISMA_SECRET} under services >prisma > environment.
  3. Run prisma deploy and generate a prisma token with prisma token.
  4. Run yarn start again.
  5. Add the Authorization HTTP header in the playground of the API server.
{
  "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7InNlcnZpY2UiOiJkZWZhdWx0QGRlZmF1bHQiLCJyb2xlcyI6WyJhZG1pbiJdfSwiaWF0IjoxNTU3NzQ4NDAzLCJleHAiOjE1NTgzNTMyMDN9.I8C7K_Q7EPtN6QnTH9yNyrA3oHqMXWjn9znqGKaIcxA"
}

Run any simple query.

Finally,

{
  "data": null,
  "errors": [
    {
      "message": "Your token is invalid. It might have expired or you might be using a token from a different project.",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "users"
      ],
      "code": 3015,
      "requestId": "local:cjvmbfyay00070878ixcq716i"
    }
  ]
}

I see. But is your API running on your local host or in a docker container?

Because it's your API - on top of prisma - that needs to get the PRISMA_SECRET.

I explain myself:

Prisma runs in a docker container, and you provide it the PRISMA_SECRET env variable. That's perfect.
Like so, your prisma playground and prisma admin will be able to query the graphql API of prisma.

Now, you have your own API that provides a part of the prisma API (and some extra queries and mutations).
If you followed the prisma.io tutorial, you're running your API on your local host (in other words, you're not running it in docker or a virtual machine).

I did not follow the prisma.io tutorial as I didn't want to run my API on my local host.
Instead of this, I've created a docker-compose service for my API, so I ended up with something like this:

version: '3.4'

services:
  api:
    [...]

  prisma:
    [...]

  db:
    [...]

As my api service now runs in its own docker container, it needs to access to PRISMA_SECRET as well (or else it won't be able to decode the ${process.env["PRISMA_SECRET"]}.

So if your api runs in docker, you must provide it the PRISMA_SECRET in the same way you did it for the prisma service.

But if you did not use docker for your api, you must export the PRISMA_SECRET in your terminal before executing your yarn start command:

$ export PRISMA_SECRET="someprismasecret"
$ yarn start

Now, I'm not really an expert with .env files, and it seems like it should already work without needing to export the variables.
Maybe try to console.log(process.env) in order to see if it the env variables get loaded.

Thanks for the explanation, the .env variables are loaded just fine so that's not the issue. I understand the three layers I have for my Prisma server: database < graphql server < api layer, but I think your approach to dockerizing the api is a really good one.

Do you mind sharing the steps you took in doing so, so I could try that instead?

@anthonykrivonos

Your issue might be related to system time. Try syncing up your computer's time with an NTP.

Heads up for folks finding this from google:

You need to load your dotenv before importing prisma

GOOD:

import { config } from "dotenv";
config();
import { prisma } from "./generated/prisma-client";

BAD:

import { prisma } from "./generated/prisma-client";
import { config } from "dotenv";
config();

Why?

Prisma's generated client runs a constructor that grabs your env variables in the root scope, so when you import, prisma will look at your env variables. And since you haven't loaded them in yet, prisma won't be able to generate a proper token for you.

export const Prisma = makePrismaClientClass<ClientConstructor<Prisma>>({
  typeDefs,
  models,
  endpoint: `https://your-endpoint.com`,
  secret: `${process.env["YOUR_SECRET"]}`
});

@Flaque that was it, thanks a million 馃憣

@Flaque Thank you!!!!!

Hey @Flaque !
Tried out https://github.com/prisma/prisma/issues/4492#issuecomment-495742958...

Didn't work for me. Same issue here.

I'm facing this issue right now, can't signup from playground!

Same Issue here as Omar

Same for me. No solutions seem to be working. Any fix?

Same issue here

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sedubois picture sedubois  路  3Comments

Fi1osof picture Fi1osof  路  3Comments

akoenig picture akoenig  路  3Comments

ragnorc picture ragnorc  路  3Comments

schickling picture schickling  路  3Comments