Repository: Volst/graphql-authentication Branch: master Commit: a8f3c1a9050f Files: 94 Total size: 163.0 KB Directory structure: gitextract_m2pa_iuq/ ├── .editorconfig ├── .gitignore ├── .prettierrc ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── examples/ │ ├── with-prisma/ │ │ ├── .graphqlconfig.yml │ │ ├── README.md │ │ ├── datamodel.graphql │ │ ├── docker-compose.yml │ │ ├── emails/ │ │ │ ├── inviteUser/ │ │ │ │ ├── html.pug │ │ │ │ └── subject.pug │ │ │ ├── passwordReset/ │ │ │ │ ├── html.pug │ │ │ │ └── subject.pug │ │ │ └── signupUser/ │ │ │ ├── html.pug │ │ │ └── subject.pug │ │ ├── generated/ │ │ │ ├── prisma.graphql │ │ │ └── prisma.ts │ │ ├── package.json │ │ ├── prisma.yml │ │ ├── schema.graphql │ │ ├── server.ts │ │ └── utils.ts │ ├── with-sequelize/ │ │ ├── README.md │ │ ├── SequelizeAdapter.js │ │ ├── database.js │ │ ├── emails/ │ │ │ ├── inviteUser/ │ │ │ │ ├── html.pug │ │ │ │ └── subject.pug │ │ │ ├── passwordReset/ │ │ │ │ ├── html.pug │ │ │ │ └── subject.pug │ │ │ └── signupUser/ │ │ │ ├── html.pug │ │ │ └── subject.pug │ │ ├── package.json │ │ ├── schema.graphql │ │ └── server.js │ └── with-typeorm/ │ ├── README.md │ ├── TypeOrmAdapter.ts │ ├── database.ts │ ├── emails/ │ │ ├── inviteUser/ │ │ │ ├── html.pug │ │ │ └── subject.pug │ │ ├── passwordReset/ │ │ │ ├── html.pug │ │ │ └── subject.pug │ │ └── signupUser/ │ │ ├── html.pug │ │ └── subject.pug │ ├── entities/ │ │ └── User.ts │ ├── ormconfig.json │ ├── package.json │ ├── schema.graphql │ ├── server.ts │ └── tsconfig.json ├── lerna.json ├── live-demo/ │ ├── InMemoryAdapter.js │ ├── README.md │ ├── email.js │ ├── emails/ │ │ ├── inviteUser/ │ │ │ ├── html.pug │ │ │ └── subject.pug │ │ ├── passwordReset/ │ │ │ ├── html.pug │ │ │ └── subject.pug │ │ └── signupUser/ │ │ ├── html.pug │ │ └── subject.pug │ ├── index.js │ ├── package.json │ └── schema.graphql ├── package.json ├── packages/ │ ├── graphql-authentication/ │ │ ├── README.md │ │ ├── package.json │ │ ├── schema.graphql │ │ ├── src/ │ │ │ ├── Adapter.ts │ │ │ ├── Config.ts │ │ │ ├── __tests__/ │ │ │ │ ├── mutations.ts │ │ │ │ ├── queries.ts │ │ │ │ └── setup.ts │ │ │ ├── binding.ts │ │ │ ├── errors.ts │ │ │ ├── index.ts │ │ │ ├── mutations.ts │ │ │ ├── queries.ts │ │ │ ├── schema.ts │ │ │ └── utils.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ └── graphql-authentication-prisma/ │ ├── README.md │ ├── package.json │ ├── src/ │ │ ├── Prisma.ts │ │ ├── generated/ │ │ │ ├── prisma.graphql │ │ │ └── prisma.ts │ │ ├── index.ts │ │ └── utils.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── tsconfig.base.json ├── tsconfig.json └── tslint.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # editorconfig.org root = true [*] indent_style = space indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.md] trim_trailing_whitespace = false ================================================ FILE: .gitignore ================================================ node_modules dist *.log coverage .env ================================================ FILE: .prettierrc ================================================ { "singleQuote": true } ================================================ FILE: .travis.yml ================================================ sudo: false language: node_js node_js: - '8' cache: yarn: true directories: - node_modules script: cd packages/graphql-authentication && npm run ci ================================================ FILE: CONTRIBUTING.md ================================================ Pull requests and issues are very welcome! # Prerequisites - Node v8+ # Getting started To get started, clone this repository and run `yarn` in the repository root. Note you have to use Yarn, npm will not work correctly. In the `packages/` folder, pick one of the packages you want to work on. You can run the tests inside a package with `yarn test`. If you want to use a GraphQL Playground to test something by hand, you can go to `packages/graphql-authentication-prisma/example` and run `docker-compose up -d` in it (this requires you have Docker installed). Then, run `yarn start`. # Adding a feature or changing behavior For features or changes, please create a new issue first. Since this is a very opinionated package, it is possible I don’t like the change. By discussing it first you can prevent wasted time. But please do! I am very open to improvements. # Publishing packages In the root repository, run `yarn run publish` (note: the `run` part is very important). This will make Lerna publish all the packages. After that, be sure to write a changelog on GitHubs Releases page. ================================================ FILE: LICENSE ================================================ ISC License Copyright (c) 2018, Volst Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ================================================ FILE: README.md ================================================ # GraphQL Authentication **Disclaimer: This repository is not actively maintained!** _Previously called Prisma Auth_ A very opinionated user authentication package for [GraphQL](https://graphql.org/). It uses old-school email/password authentication. This package provides **a GraphQL schema and GraphQL resolvers** for everything you need related to authentication. It does not access your data layer (e.g. an ORM); for that you need to write an _adapter_ (which is not hard to do). If you use Prisma, there is already an adapter for you, **[graphql-authentication-prisma](https://github.com/Volst/graphql-authentication/tree/master/packages/graphql-authentication-prisma)**. You can also checkout the [`examples/`](https://github.com/Volst/graphql-authentication/tree/master/examples) folder with examples on how to write an adapter for Sequelize and TypeORM! [**👉 Try out the live demo**](https://graphql-authentication-demo.now.sh/) **Features:** - Signup with good ol' email/password and confirmation email - Login - Invite another user (sends email) - Password reset - Change password of current user - Update current user info - Support for [graphql-shield](https://github.com/maticzav/graphql-shield) to deal with permissions # Motivation Adding user authentication seems simple; there are lots of examples on how to write a "login" and a "signup" resolver. You implement it in your own project and continue working. After a while you'll have users forgetting their password so you need to build something for that. Then you want to be able to invite users, ... you get the idea. In the end you have a lot of boilerplate code related to user authentication. The intention with this package is **to let you write as less user-related code as possible**, while being flexible enough to support different use cases like open sign up, invitation-only signup, extra fields on the User model etc. > If this package is too opinionated for you, you could still copy/paste parts of it in your application! # Install Node v8+ should be used. Install with Yarn or npm: ``` yarn add graphql-authentication email-templates npm i graphql-authentication email-templates ``` # Usage ## Using the schema In your own GraphQL schema you can import all the types this package provides: ```graphql # import Query.*, Mutation.* from "node_modules/graphql-authentication/schema.graphql" ``` > This only works if you use [graphql-import](https://github.com/prismagraphql/graphql-import). If you are using graphql-yoga this will work out of the box! Alternatively you can only import the types you want to expose, for example: ```graphql # import Query.currentUser, Mutation.signupByInvite, Mutation.inviteUser, Mutation.login from "node_modules/graphql-authentication/schema.graphql" ``` ## Configuration We need to add some configuration to get this package to work. The following example uses [graphql-yoga](https://github.com/graphcool/graphql-yoga/), but it should also work with Apollo Server. ```js import { graphqlAuthenticationConfig } from 'graphql-authentication'; import * as Email from 'email-templates'; const server = new GraphQLServer({ typeDefs: './schema.graphql', resolvers, context: req => ({ ...req, graphqlAuthentication: graphqlAuthenticationConfig({ // Required, see for more info the "Writing an adapter" section on this page adapter: new GraphqlAuthenticationSequelizeAdapter(), // Required, used for signing JWT tokens secret: 'wheredidthesodago', // Optional, for sending emails with email-templates (https://www.npmjs.com/package/email-templates) mailer: new Email(), // Optional, the URL to your frontend which is used in emails mailAppUrl: 'http://example.com' }) }) }); ``` ## Adding the resolvers You need to expose the resolvers this package provides for you to your own GraphQL server. For example: ```js import { authQueries, authMutations } from 'graphql-authentication'; const resolvers = { Query: { ...authQueries }, Mutation: { ...authMutations } }; ``` ## Emails Lastly, this project can optionally send emails for you (e.g. the password reset link). [`email-templates`](https://www.npmjs.com/package/email-templates) is used for this. Be sure to configure it in the options: ```js import * as Email from 'email-templates'; graphqlAuthentication: graphqlAuthenticationConfig({ mailer: new Email() }); ``` However, this package does not provide the email templates itself for you, since these differ too much. You can [**copy the email templates**](https://github.com/Volst/graphql-authentication/tree/master/examples/with-prisma/emails) from our example to get started. # Documentation ## GraphQL endpoints Mutations: - `signUpByInvite` - `signup` - `confirmEmail` - `inviteUser` - `login` - `changePassword` - `updateCurrentUser` - `trigerPasswordReset` - `passwordReset` Queries: - `currentUser` For more details take a look at [schema.graphql](./packages/graphql-authentication/schema.graphql). ## Authentication on endpoints On some of your endpoints you might want to require that the user is logged in, or only allow the user to see the data if they have a specific role. A very powerful package exists for this, [graphql-shield](https://github.com/maticzav/graphql-shield): ```js import { shield, rule } from 'graphql-shield'; import { isAuthResolver } from 'graphql-authentication'; const isAuth = rule()(isAuthResolver); const permissions = shield({ Mutation: { publish: isAuth } }); const server = new GraphQLServer({ typeDefs: './schema.graphql', resolvers, middlewares: [permissions] }); ``` Take a look at the [graphql-shield README](https://github.com/maticzav/graphql-shield/blob/master/README.md) to find out more. ## Helper utilities Get the current user in a resolver (performs a request to your data layer): ```js import { getUser } from 'graphql-authentication'; const Mutation = { async publish(parent, data, ctx) { const user = await getUser(ctx); console.log('User', user.email); } }; ``` Get only the current user ID in a resolver (without request to your data layer): ```js import { getUserId } from 'graphql-authentication'; const Mutation = { async publish(parent, data, ctx) { const userId = await getUserId(ctx); console.log('User', userId); } }; ``` ## Login and session handling [JWT tokens](https://jwt.io/) are used to handle sessions. In the frontend you can perform a login like this: ```graphql mutation login($email: String!, $password: String!) { login(email: $email, password: $password) { token user { # optional name } } } ``` And then save the token to `localStorage`. Now you need to send the token with every request. If you are using Apollo, [the documentation](https://www.apollographql.com/docs/react/recipes/authentication.html#Header) has a great example on how to do this. ## Adding custom fields to the User type If you wish to expose some fields on the User type that are not exposed in our [schema.graphql](./packages/graphql-authentication/schema.graphql), you can provide your own User. In your own `schema.graphql`, do something like the following: ```graphql # import Mutation.* from "node_modules/graphql-authentication/schema.graphql" type Query { currentUser: User } type User { id: ID! email: String! name: String! inviteAccepted: Boolean! emailConfirmed: Boolean! deletedAt: DateTime lastLogin: DateTime joinedAt: DateTime! isSuper: Boolean! # And finally, our custom field: isWillingToDance: Boolean! } ``` If for example you do not want the `joinedAt` field to be exposed, you can simply remove it from your schema. > `extend type User` would save some copy/pasta here, but unfortunately that doesn't work yet in `graphql-js`. [More info](https://github.com/graphcool/graphql-import/issues/42#issuecomment-357693183). ## Signup only by invite By default everyone can signup for your project. But what if you want to only allow invite by signup? In this case you need to leave out the `Mutation.signup` import. Example: ```graphql # import Mutation.signupByInvite, Mutation.inviteUser, Mutation.login, Mutation.changePassword, Mutation.updateCurrentUser, Mutation.triggerPasswordReset, Mutation.passwordReset, from "node_modules/graphql-authentication/schema.graphql" ``` ## Making email confirmation required before login After a user signups via the `signup` endpoint, they will get an email with a link in it to confirm their email. Meanwhile they can still login in the app. This is done to not disturb the users flow too much (e.g. services like Twitter do this too). It is left open to the project to block the user after a while. With the fields `emailConfirmed` and `joinedAt` on the User you can perhaps display a warning in your frontend or disallow certain features. However, you might want to block the user from logging in at all when their email is not yet confirmed. In this case you need to pass this option: ```js graphqlAuthentication: graphqlAuthenticationConfig({ requiredConfirmedEmailForLogin: true }); ``` ## Custom password validation The users password is validated with `password.length >= 8` by default. Maybe you want stricter or less stricter validation on this. You will need to pass this option to change it: ```js graphqlAuthentication: graphqlAuthenticationConfig({ validatePassword: value => value.length >= 10 }); ``` ## Writing an adapter An adapter sits between GraphQL Authentication and your own ORM/database thingy. If you are using Prisma, there is already [graphql-authentication-prisma](https://github.com/Volst/graphql-authentication/tree/master/packages/graphql-authentication-prisma) for you. However, if you don't use Prisma, that's totally fine! Writing an adapter shouldn't take very long. In [the tests](https://github.com/Volst/graphql-authentication/blob/refactor/packages/graphql-authentication/src/__tests__/setup.ts) there is a good example of an adapter. Also, the [`examples/`](https://github.com/Volst/graphql-authentication/tree/master/examples) folder contains more some custom adapters, like one for Sequelize. You can keep the adapter class directly in your own project, make a separate npm package for it or write a PR to add it here (please do)! > TODO: this section needs to be improved ================================================ FILE: examples/with-prisma/.graphqlconfig.yml ================================================ projects: prisma: schemaPath: "./generated/prisma.graphql" extensions: prisma: prisma.yml codegen: generator: prisma-binding output: binding: "generated/prisma.ts" language: typescript ================================================ FILE: examples/with-prisma/README.md ================================================ # Prisma example This is an example of how to use GraphQL Authentication with [Prisma](https://www.prisma.io/). It uses the adapter package for Prisma, [graphql-authentication-prisma](https://github.com/Volst/graphql-authentication/tree/master/packages/graphql-authentication-prisma). ## Usage You need to have Docker installed. If you're new to Prisma, you might want to read the [Quickstart](https://www.prismagraphql.com/docs/quickstart/) first. ``` docker-compose up -d # now wait for it to start yarn prisma deploy npm i npm start ``` Go to http://localhost:4000 in your browser and start running some queries and mutations! ================================================ FILE: examples/with-prisma/datamodel.graphql ================================================ type User { id: ID! @unique email: String! @unique password: String! name: String! inviteToken: String inviteAccepted: Boolean! @default(value: "true") emailConfirmed: Boolean! @default(value: "true") emailConfirmToken: String resetToken: String resetExpires: DateTime deletedAt: DateTime lastLogin: DateTime joinedAt: DateTime! isSuper: Boolean! @default(value: "false") } ================================================ FILE: examples/with-prisma/docker-compose.yml ================================================ version: '3' services: prisma: image: prismagraphql/prisma:1.9 restart: "no" ports: - "4466:4466" environment: PRISMA_CONFIG: | port: 4466 managementApiSecret: "mymanagementsecret123" databases: default: connector: mysql active: true host: db port: 3306 user: root password: prisma db: image: mysql:5.7 restart: "no" environment: MYSQL_ROOT_PASSWORD: prisma ================================================ FILE: examples/with-prisma/emails/inviteUser/html.pug ================================================ p Hi, p You are invited to join *your project*! p a(href=mailAppUrl + '/register/' + email + '/' + inviteToken) Accept invite. ================================================ FILE: examples/with-prisma/emails/inviteUser/subject.pug ================================================ = `Invited for *your project*!` ================================================ FILE: examples/with-prisma/emails/passwordReset/html.pug ================================================ p Hi, p You requested a password reset on *your project*. p a(href=mailAppUrl + '/login/reset-password/' + email + '/' + resetToken) Reset my password. ================================================ FILE: examples/with-prisma/emails/passwordReset/subject.pug ================================================ = `Confirm your email on *your project*` ================================================ FILE: examples/with-prisma/emails/signupUser/html.pug ================================================ p Hi, p You have just created an account on *your project*! As a last step, please confirm your email: p a(href=mailAppUrl + '/confirm-email/' + email + '/' + emailConfirmToken) Confirm email. ================================================ FILE: examples/with-prisma/emails/signupUser/subject.pug ================================================ = `Confirm your email on *your project*` ================================================ FILE: examples/with-prisma/generated/prisma.graphql ================================================ # source: http://localhost:4466 # timestamp: Mon Jul 16 2018 12:37:56 GMT+0200 (CEST) type AggregateUser { count: Int! } type BatchPayload { """ The number of nodes that have been affected by the Batch operation. """ count: Long! } scalar DateTime """ The `Long` scalar type represents non-fractional signed whole numeric values. Long can represent values between -(2^63) and 2^63 - 1. """ scalar Long type Mutation { createUser(data: UserCreateInput!): User! updateUser(data: UserUpdateInput!, where: UserWhereUniqueInput!): User deleteUser(where: UserWhereUniqueInput!): User upsertUser( where: UserWhereUniqueInput! create: UserCreateInput! update: UserUpdateInput! ): User! updateManyUsers(data: UserUpdateInput!, where: UserWhereInput): BatchPayload! deleteManyUsers(where: UserWhereInput): BatchPayload! } enum MutationType { CREATED UPDATED DELETED } """ An object with an ID """ interface Node { """ The id of the object. """ id: ID! } """ Information about pagination in a connection. """ type PageInfo { """ When paginating forwards, are there more items? """ hasNextPage: Boolean! """ When paginating backwards, are there more items? """ hasPreviousPage: Boolean! """ When paginating backwards, the cursor to continue. """ startCursor: String """ When paginating forwards, the cursor to continue. """ endCursor: String } type Query { users( where: UserWhereInput orderBy: UserOrderByInput skip: Int after: String before: String first: Int last: Int ): [User]! user(where: UserWhereUniqueInput!): User usersConnection( where: UserWhereInput orderBy: UserOrderByInput skip: Int after: String before: String first: Int last: Int ): UserConnection! """ Fetches an object given its ID """ node( """ The ID of an object """ id: ID! ): Node } type Subscription { user(where: UserSubscriptionWhereInput): UserSubscriptionPayload } type User implements Node { id: ID! email: String! password: String! name: String! inviteToken: String inviteAccepted: Boolean! emailConfirmed: Boolean! emailConfirmToken: String resetToken: String resetExpires: DateTime deletedAt: DateTime lastLogin: DateTime joinedAt: DateTime! isSuper: Boolean! } """ A connection to a list of items. """ type UserConnection { """ Information to aid in pagination. """ pageInfo: PageInfo! """ A list of edges. """ edges: [UserEdge]! aggregate: AggregateUser! } input UserCreateInput { email: String! password: String! name: String! inviteToken: String inviteAccepted: Boolean emailConfirmed: Boolean emailConfirmToken: String resetToken: String resetExpires: DateTime deletedAt: DateTime lastLogin: DateTime joinedAt: DateTime! isSuper: Boolean } """ An edge in a connection. """ type UserEdge { """ The item at the end of the edge. """ node: User! """ A cursor for use in pagination. """ cursor: String! } enum UserOrderByInput { id_ASC id_DESC email_ASC email_DESC password_ASC password_DESC name_ASC name_DESC inviteToken_ASC inviteToken_DESC inviteAccepted_ASC inviteAccepted_DESC emailConfirmed_ASC emailConfirmed_DESC emailConfirmToken_ASC emailConfirmToken_DESC resetToken_ASC resetToken_DESC resetExpires_ASC resetExpires_DESC deletedAt_ASC deletedAt_DESC lastLogin_ASC lastLogin_DESC joinedAt_ASC joinedAt_DESC isSuper_ASC isSuper_DESC updatedAt_ASC updatedAt_DESC createdAt_ASC createdAt_DESC } type UserPreviousValues { id: ID! email: String! password: String! name: String! inviteToken: String inviteAccepted: Boolean! emailConfirmed: Boolean! emailConfirmToken: String resetToken: String resetExpires: DateTime deletedAt: DateTime lastLogin: DateTime joinedAt: DateTime! isSuper: Boolean! } type UserSubscriptionPayload { mutation: MutationType! node: User updatedFields: [String!] previousValues: UserPreviousValues } input UserSubscriptionWhereInput { """ Logical AND on all given filters. """ AND: [UserSubscriptionWhereInput!] """ Logical OR on all given filters. """ OR: [UserSubscriptionWhereInput!] """ Logical NOT on all given filters combined by AND. """ NOT: [UserSubscriptionWhereInput!] """ The subscription event gets dispatched when it's listed in mutation_in """ mutation_in: [MutationType!] """ The subscription event gets only dispatched when one of the updated fields names is included in this list """ updatedFields_contains: String """ The subscription event gets only dispatched when all of the field names included in this list have been updated """ updatedFields_contains_every: [String!] """ The subscription event gets only dispatched when some of the field names included in this list have been updated """ updatedFields_contains_some: [String!] node: UserWhereInput } input UserUpdateInput { email: String password: String name: String inviteToken: String inviteAccepted: Boolean emailConfirmed: Boolean emailConfirmToken: String resetToken: String resetExpires: DateTime deletedAt: DateTime lastLogin: DateTime joinedAt: DateTime isSuper: Boolean } input UserWhereInput { """ Logical AND on all given filters. """ AND: [UserWhereInput!] """ Logical OR on all given filters. """ OR: [UserWhereInput!] """ Logical NOT on all given filters combined by AND. """ NOT: [UserWhereInput!] id: ID """ All values that are not equal to given value. """ id_not: ID """ All values that are contained in given list. """ id_in: [ID!] """ All values that are not contained in given list. """ id_not_in: [ID!] """ All values less than the given value. """ id_lt: ID """ All values less than or equal the given value. """ id_lte: ID """ All values greater than the given value. """ id_gt: ID """ All values greater than or equal the given value. """ id_gte: ID """ All values containing the given string. """ id_contains: ID """ All values not containing the given string. """ id_not_contains: ID """ All values starting with the given string. """ id_starts_with: ID """ All values not starting with the given string. """ id_not_starts_with: ID """ All values ending with the given string. """ id_ends_with: ID """ All values not ending with the given string. """ id_not_ends_with: ID email: String """ All values that are not equal to given value. """ email_not: String """ All values that are contained in given list. """ email_in: [String!] """ All values that are not contained in given list. """ email_not_in: [String!] """ All values less than the given value. """ email_lt: String """ All values less than or equal the given value. """ email_lte: String """ All values greater than the given value. """ email_gt: String """ All values greater than or equal the given value. """ email_gte: String """ All values containing the given string. """ email_contains: String """ All values not containing the given string. """ email_not_contains: String """ All values starting with the given string. """ email_starts_with: String """ All values not starting with the given string. """ email_not_starts_with: String """ All values ending with the given string. """ email_ends_with: String """ All values not ending with the given string. """ email_not_ends_with: String password: String """ All values that are not equal to given value. """ password_not: String """ All values that are contained in given list. """ password_in: [String!] """ All values that are not contained in given list. """ password_not_in: [String!] """ All values less than the given value. """ password_lt: String """ All values less than or equal the given value. """ password_lte: String """ All values greater than the given value. """ password_gt: String """ All values greater than or equal the given value. """ password_gte: String """ All values containing the given string. """ password_contains: String """ All values not containing the given string. """ password_not_contains: String """ All values starting with the given string. """ password_starts_with: String """ All values not starting with the given string. """ password_not_starts_with: String """ All values ending with the given string. """ password_ends_with: String """ All values not ending with the given string. """ password_not_ends_with: String name: String """ All values that are not equal to given value. """ name_not: String """ All values that are contained in given list. """ name_in: [String!] """ All values that are not contained in given list. """ name_not_in: [String!] """ All values less than the given value. """ name_lt: String """ All values less than or equal the given value. """ name_lte: String """ All values greater than the given value. """ name_gt: String """ All values greater than or equal the given value. """ name_gte: String """ All values containing the given string. """ name_contains: String """ All values not containing the given string. """ name_not_contains: String """ All values starting with the given string. """ name_starts_with: String """ All values not starting with the given string. """ name_not_starts_with: String """ All values ending with the given string. """ name_ends_with: String """ All values not ending with the given string. """ name_not_ends_with: String inviteToken: String """ All values that are not equal to given value. """ inviteToken_not: String """ All values that are contained in given list. """ inviteToken_in: [String!] """ All values that are not contained in given list. """ inviteToken_not_in: [String!] """ All values less than the given value. """ inviteToken_lt: String """ All values less than or equal the given value. """ inviteToken_lte: String """ All values greater than the given value. """ inviteToken_gt: String """ All values greater than or equal the given value. """ inviteToken_gte: String """ All values containing the given string. """ inviteToken_contains: String """ All values not containing the given string. """ inviteToken_not_contains: String """ All values starting with the given string. """ inviteToken_starts_with: String """ All values not starting with the given string. """ inviteToken_not_starts_with: String """ All values ending with the given string. """ inviteToken_ends_with: String """ All values not ending with the given string. """ inviteToken_not_ends_with: String inviteAccepted: Boolean """ All values that are not equal to given value. """ inviteAccepted_not: Boolean emailConfirmed: Boolean """ All values that are not equal to given value. """ emailConfirmed_not: Boolean emailConfirmToken: String """ All values that are not equal to given value. """ emailConfirmToken_not: String """ All values that are contained in given list. """ emailConfirmToken_in: [String!] """ All values that are not contained in given list. """ emailConfirmToken_not_in: [String!] """ All values less than the given value. """ emailConfirmToken_lt: String """ All values less than or equal the given value. """ emailConfirmToken_lte: String """ All values greater than the given value. """ emailConfirmToken_gt: String """ All values greater than or equal the given value. """ emailConfirmToken_gte: String """ All values containing the given string. """ emailConfirmToken_contains: String """ All values not containing the given string. """ emailConfirmToken_not_contains: String """ All values starting with the given string. """ emailConfirmToken_starts_with: String """ All values not starting with the given string. """ emailConfirmToken_not_starts_with: String """ All values ending with the given string. """ emailConfirmToken_ends_with: String """ All values not ending with the given string. """ emailConfirmToken_not_ends_with: String resetToken: String """ All values that are not equal to given value. """ resetToken_not: String """ All values that are contained in given list. """ resetToken_in: [String!] """ All values that are not contained in given list. """ resetToken_not_in: [String!] """ All values less than the given value. """ resetToken_lt: String """ All values less than or equal the given value. """ resetToken_lte: String """ All values greater than the given value. """ resetToken_gt: String """ All values greater than or equal the given value. """ resetToken_gte: String """ All values containing the given string. """ resetToken_contains: String """ All values not containing the given string. """ resetToken_not_contains: String """ All values starting with the given string. """ resetToken_starts_with: String """ All values not starting with the given string. """ resetToken_not_starts_with: String """ All values ending with the given string. """ resetToken_ends_with: String """ All values not ending with the given string. """ resetToken_not_ends_with: String resetExpires: DateTime """ All values that are not equal to given value. """ resetExpires_not: DateTime """ All values that are contained in given list. """ resetExpires_in: [DateTime!] """ All values that are not contained in given list. """ resetExpires_not_in: [DateTime!] """ All values less than the given value. """ resetExpires_lt: DateTime """ All values less than or equal the given value. """ resetExpires_lte: DateTime """ All values greater than the given value. """ resetExpires_gt: DateTime """ All values greater than or equal the given value. """ resetExpires_gte: DateTime deletedAt: DateTime """ All values that are not equal to given value. """ deletedAt_not: DateTime """ All values that are contained in given list. """ deletedAt_in: [DateTime!] """ All values that are not contained in given list. """ deletedAt_not_in: [DateTime!] """ All values less than the given value. """ deletedAt_lt: DateTime """ All values less than or equal the given value. """ deletedAt_lte: DateTime """ All values greater than the given value. """ deletedAt_gt: DateTime """ All values greater than or equal the given value. """ deletedAt_gte: DateTime lastLogin: DateTime """ All values that are not equal to given value. """ lastLogin_not: DateTime """ All values that are contained in given list. """ lastLogin_in: [DateTime!] """ All values that are not contained in given list. """ lastLogin_not_in: [DateTime!] """ All values less than the given value. """ lastLogin_lt: DateTime """ All values less than or equal the given value. """ lastLogin_lte: DateTime """ All values greater than the given value. """ lastLogin_gt: DateTime """ All values greater than or equal the given value. """ lastLogin_gte: DateTime joinedAt: DateTime """ All values that are not equal to given value. """ joinedAt_not: DateTime """ All values that are contained in given list. """ joinedAt_in: [DateTime!] """ All values that are not contained in given list. """ joinedAt_not_in: [DateTime!] """ All values less than the given value. """ joinedAt_lt: DateTime """ All values less than or equal the given value. """ joinedAt_lte: DateTime """ All values greater than the given value. """ joinedAt_gt: DateTime """ All values greater than or equal the given value. """ joinedAt_gte: DateTime isSuper: Boolean """ All values that are not equal to given value. """ isSuper_not: Boolean } input UserWhereUniqueInput { id: ID email: String } ================================================ FILE: examples/with-prisma/generated/prisma.ts ================================================ import { GraphQLResolveInfo, GraphQLSchema } from 'graphql'; import { IResolvers } from 'graphql-tools/dist/Interfaces'; import { Options } from 'graphql-binding'; import { makePrismaBindingClass, BasePrismaOptions } from 'prisma-binding'; export interface Query { users: ( args: { where?: UserWhereInput; orderBy?: UserOrderByInput; skip?: Int; after?: String; before?: String; first?: Int; last?: Int; }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; user: ( args: { where: UserWhereUniqueInput }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; usersConnection: ( args: { where?: UserWhereInput; orderBy?: UserOrderByInput; skip?: Int; after?: String; before?: String; first?: Int; last?: Int; }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; node: ( args: { id: ID_Output }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; } export interface Mutation { createUser: ( args: { data: UserCreateInput }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; updateUser: ( args: { data: UserUpdateInput; where: UserWhereUniqueInput }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; deleteUser: ( args: { where: UserWhereUniqueInput }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; upsertUser: ( args: { where: UserWhereUniqueInput; create: UserCreateInput; update: UserUpdateInput; }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; updateManyUsers: ( args: { data: UserUpdateInput; where?: UserWhereInput }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; deleteManyUsers: ( args: { where?: UserWhereInput }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; } export interface Subscription { user: ( args: { where?: UserSubscriptionWhereInput }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise>; } export interface Exists { User: (where?: UserWhereInput) => Promise; } export interface Prisma { query: Query; mutation: Mutation; subscription: Subscription; exists: Exists; request: ( query: string, variables?: { [key: string]: any } ) => Promise; delegate( operation: 'query' | 'mutation', fieldName: string, args: { [key: string]: any; }, infoOrQuery?: GraphQLResolveInfo | string, options?: Options ): Promise; delegateSubscription( fieldName: string, args?: { [key: string]: any; }, infoOrQuery?: GraphQLResolveInfo | string, options?: Options ): Promise>; getAbstractResolvers(filterSchema?: GraphQLSchema | string): IResolvers; } export interface BindingConstructor { new (options: BasePrismaOptions): T; } /** * Type Defs */ const typeDefs = `type AggregateUser { count: Int! } type BatchPayload { """The number of nodes that have been affected by the Batch operation.""" count: Long! } scalar DateTime """ The \`Long\` scalar type represents non-fractional signed whole numeric values. Long can represent values between -(2^63) and 2^63 - 1. """ scalar Long type Mutation { createUser(data: UserCreateInput!): User! updateUser(data: UserUpdateInput!, where: UserWhereUniqueInput!): User deleteUser(where: UserWhereUniqueInput!): User upsertUser(where: UserWhereUniqueInput!, create: UserCreateInput!, update: UserUpdateInput!): User! updateManyUsers(data: UserUpdateInput!, where: UserWhereInput): BatchPayload! deleteManyUsers(where: UserWhereInput): BatchPayload! } enum MutationType { CREATED UPDATED DELETED } """An object with an ID""" interface Node { """The id of the object.""" id: ID! } """Information about pagination in a connection.""" type PageInfo { """When paginating forwards, are there more items?""" hasNextPage: Boolean! """When paginating backwards, are there more items?""" hasPreviousPage: Boolean! """When paginating backwards, the cursor to continue.""" startCursor: String """When paginating forwards, the cursor to continue.""" endCursor: String } type Query { users(where: UserWhereInput, orderBy: UserOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [User]! user(where: UserWhereUniqueInput!): User usersConnection(where: UserWhereInput, orderBy: UserOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): UserConnection! """Fetches an object given its ID""" node( """The ID of an object""" id: ID! ): Node } type Subscription { user(where: UserSubscriptionWhereInput): UserSubscriptionPayload } type User implements Node { id: ID! email: String! password: String! name: String! inviteToken: String inviteAccepted: Boolean! emailConfirmed: Boolean! emailConfirmToken: String resetToken: String resetExpires: DateTime deletedAt: DateTime lastLogin: DateTime joinedAt: DateTime! isSuper: Boolean! } """A connection to a list of items.""" type UserConnection { """Information to aid in pagination.""" pageInfo: PageInfo! """A list of edges.""" edges: [UserEdge]! aggregate: AggregateUser! } input UserCreateInput { email: String! password: String! name: String! inviteToken: String inviteAccepted: Boolean emailConfirmed: Boolean emailConfirmToken: String resetToken: String resetExpires: DateTime deletedAt: DateTime lastLogin: DateTime joinedAt: DateTime! isSuper: Boolean } """An edge in a connection.""" type UserEdge { """The item at the end of the edge.""" node: User! """A cursor for use in pagination.""" cursor: String! } enum UserOrderByInput { id_ASC id_DESC email_ASC email_DESC password_ASC password_DESC name_ASC name_DESC inviteToken_ASC inviteToken_DESC inviteAccepted_ASC inviteAccepted_DESC emailConfirmed_ASC emailConfirmed_DESC emailConfirmToken_ASC emailConfirmToken_DESC resetToken_ASC resetToken_DESC resetExpires_ASC resetExpires_DESC deletedAt_ASC deletedAt_DESC lastLogin_ASC lastLogin_DESC joinedAt_ASC joinedAt_DESC isSuper_ASC isSuper_DESC updatedAt_ASC updatedAt_DESC createdAt_ASC createdAt_DESC } type UserPreviousValues { id: ID! email: String! password: String! name: String! inviteToken: String inviteAccepted: Boolean! emailConfirmed: Boolean! emailConfirmToken: String resetToken: String resetExpires: DateTime deletedAt: DateTime lastLogin: DateTime joinedAt: DateTime! isSuper: Boolean! } type UserSubscriptionPayload { mutation: MutationType! node: User updatedFields: [String!] previousValues: UserPreviousValues } input UserSubscriptionWhereInput { """Logical AND on all given filters.""" AND: [UserSubscriptionWhereInput!] """Logical OR on all given filters.""" OR: [UserSubscriptionWhereInput!] """Logical NOT on all given filters combined by AND.""" NOT: [UserSubscriptionWhereInput!] """ The subscription event gets dispatched when it's listed in mutation_in """ mutation_in: [MutationType!] """ The subscription event gets only dispatched when one of the updated fields names is included in this list """ updatedFields_contains: String """ The subscription event gets only dispatched when all of the field names included in this list have been updated """ updatedFields_contains_every: [String!] """ The subscription event gets only dispatched when some of the field names included in this list have been updated """ updatedFields_contains_some: [String!] node: UserWhereInput } input UserUpdateInput { email: String password: String name: String inviteToken: String inviteAccepted: Boolean emailConfirmed: Boolean emailConfirmToken: String resetToken: String resetExpires: DateTime deletedAt: DateTime lastLogin: DateTime joinedAt: DateTime isSuper: Boolean } input UserWhereInput { """Logical AND on all given filters.""" AND: [UserWhereInput!] """Logical OR on all given filters.""" OR: [UserWhereInput!] """Logical NOT on all given filters combined by AND.""" NOT: [UserWhereInput!] id: ID """All values that are not equal to given value.""" id_not: ID """All values that are contained in given list.""" id_in: [ID!] """All values that are not contained in given list.""" id_not_in: [ID!] """All values less than the given value.""" id_lt: ID """All values less than or equal the given value.""" id_lte: ID """All values greater than the given value.""" id_gt: ID """All values greater than or equal the given value.""" id_gte: ID """All values containing the given string.""" id_contains: ID """All values not containing the given string.""" id_not_contains: ID """All values starting with the given string.""" id_starts_with: ID """All values not starting with the given string.""" id_not_starts_with: ID """All values ending with the given string.""" id_ends_with: ID """All values not ending with the given string.""" id_not_ends_with: ID email: String """All values that are not equal to given value.""" email_not: String """All values that are contained in given list.""" email_in: [String!] """All values that are not contained in given list.""" email_not_in: [String!] """All values less than the given value.""" email_lt: String """All values less than or equal the given value.""" email_lte: String """All values greater than the given value.""" email_gt: String """All values greater than or equal the given value.""" email_gte: String """All values containing the given string.""" email_contains: String """All values not containing the given string.""" email_not_contains: String """All values starting with the given string.""" email_starts_with: String """All values not starting with the given string.""" email_not_starts_with: String """All values ending with the given string.""" email_ends_with: String """All values not ending with the given string.""" email_not_ends_with: String password: String """All values that are not equal to given value.""" password_not: String """All values that are contained in given list.""" password_in: [String!] """All values that are not contained in given list.""" password_not_in: [String!] """All values less than the given value.""" password_lt: String """All values less than or equal the given value.""" password_lte: String """All values greater than the given value.""" password_gt: String """All values greater than or equal the given value.""" password_gte: String """All values containing the given string.""" password_contains: String """All values not containing the given string.""" password_not_contains: String """All values starting with the given string.""" password_starts_with: String """All values not starting with the given string.""" password_not_starts_with: String """All values ending with the given string.""" password_ends_with: String """All values not ending with the given string.""" password_not_ends_with: String name: String """All values that are not equal to given value.""" name_not: String """All values that are contained in given list.""" name_in: [String!] """All values that are not contained in given list.""" name_not_in: [String!] """All values less than the given value.""" name_lt: String """All values less than or equal the given value.""" name_lte: String """All values greater than the given value.""" name_gt: String """All values greater than or equal the given value.""" name_gte: String """All values containing the given string.""" name_contains: String """All values not containing the given string.""" name_not_contains: String """All values starting with the given string.""" name_starts_with: String """All values not starting with the given string.""" name_not_starts_with: String """All values ending with the given string.""" name_ends_with: String """All values not ending with the given string.""" name_not_ends_with: String inviteToken: String """All values that are not equal to given value.""" inviteToken_not: String """All values that are contained in given list.""" inviteToken_in: [String!] """All values that are not contained in given list.""" inviteToken_not_in: [String!] """All values less than the given value.""" inviteToken_lt: String """All values less than or equal the given value.""" inviteToken_lte: String """All values greater than the given value.""" inviteToken_gt: String """All values greater than or equal the given value.""" inviteToken_gte: String """All values containing the given string.""" inviteToken_contains: String """All values not containing the given string.""" inviteToken_not_contains: String """All values starting with the given string.""" inviteToken_starts_with: String """All values not starting with the given string.""" inviteToken_not_starts_with: String """All values ending with the given string.""" inviteToken_ends_with: String """All values not ending with the given string.""" inviteToken_not_ends_with: String inviteAccepted: Boolean """All values that are not equal to given value.""" inviteAccepted_not: Boolean emailConfirmed: Boolean """All values that are not equal to given value.""" emailConfirmed_not: Boolean emailConfirmToken: String """All values that are not equal to given value.""" emailConfirmToken_not: String """All values that are contained in given list.""" emailConfirmToken_in: [String!] """All values that are not contained in given list.""" emailConfirmToken_not_in: [String!] """All values less than the given value.""" emailConfirmToken_lt: String """All values less than or equal the given value.""" emailConfirmToken_lte: String """All values greater than the given value.""" emailConfirmToken_gt: String """All values greater than or equal the given value.""" emailConfirmToken_gte: String """All values containing the given string.""" emailConfirmToken_contains: String """All values not containing the given string.""" emailConfirmToken_not_contains: String """All values starting with the given string.""" emailConfirmToken_starts_with: String """All values not starting with the given string.""" emailConfirmToken_not_starts_with: String """All values ending with the given string.""" emailConfirmToken_ends_with: String """All values not ending with the given string.""" emailConfirmToken_not_ends_with: String resetToken: String """All values that are not equal to given value.""" resetToken_not: String """All values that are contained in given list.""" resetToken_in: [String!] """All values that are not contained in given list.""" resetToken_not_in: [String!] """All values less than the given value.""" resetToken_lt: String """All values less than or equal the given value.""" resetToken_lte: String """All values greater than the given value.""" resetToken_gt: String """All values greater than or equal the given value.""" resetToken_gte: String """All values containing the given string.""" resetToken_contains: String """All values not containing the given string.""" resetToken_not_contains: String """All values starting with the given string.""" resetToken_starts_with: String """All values not starting with the given string.""" resetToken_not_starts_with: String """All values ending with the given string.""" resetToken_ends_with: String """All values not ending with the given string.""" resetToken_not_ends_with: String resetExpires: DateTime """All values that are not equal to given value.""" resetExpires_not: DateTime """All values that are contained in given list.""" resetExpires_in: [DateTime!] """All values that are not contained in given list.""" resetExpires_not_in: [DateTime!] """All values less than the given value.""" resetExpires_lt: DateTime """All values less than or equal the given value.""" resetExpires_lte: DateTime """All values greater than the given value.""" resetExpires_gt: DateTime """All values greater than or equal the given value.""" resetExpires_gte: DateTime deletedAt: DateTime """All values that are not equal to given value.""" deletedAt_not: DateTime """All values that are contained in given list.""" deletedAt_in: [DateTime!] """All values that are not contained in given list.""" deletedAt_not_in: [DateTime!] """All values less than the given value.""" deletedAt_lt: DateTime """All values less than or equal the given value.""" deletedAt_lte: DateTime """All values greater than the given value.""" deletedAt_gt: DateTime """All values greater than or equal the given value.""" deletedAt_gte: DateTime lastLogin: DateTime """All values that are not equal to given value.""" lastLogin_not: DateTime """All values that are contained in given list.""" lastLogin_in: [DateTime!] """All values that are not contained in given list.""" lastLogin_not_in: [DateTime!] """All values less than the given value.""" lastLogin_lt: DateTime """All values less than or equal the given value.""" lastLogin_lte: DateTime """All values greater than the given value.""" lastLogin_gt: DateTime """All values greater than or equal the given value.""" lastLogin_gte: DateTime joinedAt: DateTime """All values that are not equal to given value.""" joinedAt_not: DateTime """All values that are contained in given list.""" joinedAt_in: [DateTime!] """All values that are not contained in given list.""" joinedAt_not_in: [DateTime!] """All values less than the given value.""" joinedAt_lt: DateTime """All values less than or equal the given value.""" joinedAt_lte: DateTime """All values greater than the given value.""" joinedAt_gt: DateTime """All values greater than or equal the given value.""" joinedAt_gte: DateTime isSuper: Boolean """All values that are not equal to given value.""" isSuper_not: Boolean } input UserWhereUniqueInput { id: ID email: String } `; export const Prisma = makePrismaBindingClass>({ typeDefs }); /** * Types */ export type UserOrderByInput = | 'id_ASC' | 'id_DESC' | 'email_ASC' | 'email_DESC' | 'password_ASC' | 'password_DESC' | 'name_ASC' | 'name_DESC' | 'inviteToken_ASC' | 'inviteToken_DESC' | 'inviteAccepted_ASC' | 'inviteAccepted_DESC' | 'emailConfirmed_ASC' | 'emailConfirmed_DESC' | 'emailConfirmToken_ASC' | 'emailConfirmToken_DESC' | 'resetToken_ASC' | 'resetToken_DESC' | 'resetExpires_ASC' | 'resetExpires_DESC' | 'deletedAt_ASC' | 'deletedAt_DESC' | 'lastLogin_ASC' | 'lastLogin_DESC' | 'joinedAt_ASC' | 'joinedAt_DESC' | 'isSuper_ASC' | 'isSuper_DESC' | 'updatedAt_ASC' | 'updatedAt_DESC' | 'createdAt_ASC' | 'createdAt_DESC'; export type MutationType = 'CREATED' | 'UPDATED' | 'DELETED'; export interface UserWhereUniqueInput { id?: ID_Input; email?: String; } export interface UserCreateInput { email: String; password: String; name: String; inviteToken?: String; inviteAccepted?: Boolean; emailConfirmed?: Boolean; emailConfirmToken?: String; resetToken?: String; resetExpires?: DateTime; deletedAt?: DateTime; lastLogin?: DateTime; joinedAt: DateTime; isSuper?: Boolean; } export interface UserUpdateInput { email?: String; password?: String; name?: String; inviteToken?: String; inviteAccepted?: Boolean; emailConfirmed?: Boolean; emailConfirmToken?: String; resetToken?: String; resetExpires?: DateTime; deletedAt?: DateTime; lastLogin?: DateTime; joinedAt?: DateTime; isSuper?: Boolean; } export interface UserSubscriptionWhereInput { AND?: UserSubscriptionWhereInput[] | UserSubscriptionWhereInput; OR?: UserSubscriptionWhereInput[] | UserSubscriptionWhereInput; NOT?: UserSubscriptionWhereInput[] | UserSubscriptionWhereInput; mutation_in?: MutationType[] | MutationType; updatedFields_contains?: String; updatedFields_contains_every?: String[] | String; updatedFields_contains_some?: String[] | String; node?: UserWhereInput; } export interface UserWhereInput { AND?: UserWhereInput[] | UserWhereInput; OR?: UserWhereInput[] | UserWhereInput; NOT?: UserWhereInput[] | UserWhereInput; id?: ID_Input; id_not?: ID_Input; id_in?: ID_Input[] | ID_Input; id_not_in?: ID_Input[] | ID_Input; id_lt?: ID_Input; id_lte?: ID_Input; id_gt?: ID_Input; id_gte?: ID_Input; id_contains?: ID_Input; id_not_contains?: ID_Input; id_starts_with?: ID_Input; id_not_starts_with?: ID_Input; id_ends_with?: ID_Input; id_not_ends_with?: ID_Input; email?: String; email_not?: String; email_in?: String[] | String; email_not_in?: String[] | String; email_lt?: String; email_lte?: String; email_gt?: String; email_gte?: String; email_contains?: String; email_not_contains?: String; email_starts_with?: String; email_not_starts_with?: String; email_ends_with?: String; email_not_ends_with?: String; password?: String; password_not?: String; password_in?: String[] | String; password_not_in?: String[] | String; password_lt?: String; password_lte?: String; password_gt?: String; password_gte?: String; password_contains?: String; password_not_contains?: String; password_starts_with?: String; password_not_starts_with?: String; password_ends_with?: String; password_not_ends_with?: String; name?: String; name_not?: String; name_in?: String[] | String; name_not_in?: String[] | String; name_lt?: String; name_lte?: String; name_gt?: String; name_gte?: String; name_contains?: String; name_not_contains?: String; name_starts_with?: String; name_not_starts_with?: String; name_ends_with?: String; name_not_ends_with?: String; inviteToken?: String; inviteToken_not?: String; inviteToken_in?: String[] | String; inviteToken_not_in?: String[] | String; inviteToken_lt?: String; inviteToken_lte?: String; inviteToken_gt?: String; inviteToken_gte?: String; inviteToken_contains?: String; inviteToken_not_contains?: String; inviteToken_starts_with?: String; inviteToken_not_starts_with?: String; inviteToken_ends_with?: String; inviteToken_not_ends_with?: String; inviteAccepted?: Boolean; inviteAccepted_not?: Boolean; emailConfirmed?: Boolean; emailConfirmed_not?: Boolean; emailConfirmToken?: String; emailConfirmToken_not?: String; emailConfirmToken_in?: String[] | String; emailConfirmToken_not_in?: String[] | String; emailConfirmToken_lt?: String; emailConfirmToken_lte?: String; emailConfirmToken_gt?: String; emailConfirmToken_gte?: String; emailConfirmToken_contains?: String; emailConfirmToken_not_contains?: String; emailConfirmToken_starts_with?: String; emailConfirmToken_not_starts_with?: String; emailConfirmToken_ends_with?: String; emailConfirmToken_not_ends_with?: String; resetToken?: String; resetToken_not?: String; resetToken_in?: String[] | String; resetToken_not_in?: String[] | String; resetToken_lt?: String; resetToken_lte?: String; resetToken_gt?: String; resetToken_gte?: String; resetToken_contains?: String; resetToken_not_contains?: String; resetToken_starts_with?: String; resetToken_not_starts_with?: String; resetToken_ends_with?: String; resetToken_not_ends_with?: String; resetExpires?: DateTime; resetExpires_not?: DateTime; resetExpires_in?: DateTime[] | DateTime; resetExpires_not_in?: DateTime[] | DateTime; resetExpires_lt?: DateTime; resetExpires_lte?: DateTime; resetExpires_gt?: DateTime; resetExpires_gte?: DateTime; deletedAt?: DateTime; deletedAt_not?: DateTime; deletedAt_in?: DateTime[] | DateTime; deletedAt_not_in?: DateTime[] | DateTime; deletedAt_lt?: DateTime; deletedAt_lte?: DateTime; deletedAt_gt?: DateTime; deletedAt_gte?: DateTime; lastLogin?: DateTime; lastLogin_not?: DateTime; lastLogin_in?: DateTime[] | DateTime; lastLogin_not_in?: DateTime[] | DateTime; lastLogin_lt?: DateTime; lastLogin_lte?: DateTime; lastLogin_gt?: DateTime; lastLogin_gte?: DateTime; joinedAt?: DateTime; joinedAt_not?: DateTime; joinedAt_in?: DateTime[] | DateTime; joinedAt_not_in?: DateTime[] | DateTime; joinedAt_lt?: DateTime; joinedAt_lte?: DateTime; joinedAt_gt?: DateTime; joinedAt_gte?: DateTime; isSuper?: Boolean; isSuper_not?: Boolean; } /* * An object with an ID */ export interface Node { id: ID_Output; } /* * Information about pagination in a connection. */ export interface PageInfo { hasNextPage: Boolean; hasPreviousPage: Boolean; startCursor?: String; endCursor?: String; } export interface UserPreviousValues { id: ID_Output; email: String; password: String; name: String; inviteToken?: String; inviteAccepted: Boolean; emailConfirmed: Boolean; emailConfirmToken?: String; resetToken?: String; resetExpires?: DateTime; deletedAt?: DateTime; lastLogin?: DateTime; joinedAt: DateTime; isSuper: Boolean; } export interface User extends Node { id: ID_Output; email: String; password: String; name: String; inviteToken?: String; inviteAccepted: Boolean; emailConfirmed: Boolean; emailConfirmToken?: String; resetToken?: String; resetExpires?: DateTime; deletedAt?: DateTime; lastLogin?: DateTime; joinedAt: DateTime; isSuper: Boolean; } /* * An edge in a connection. */ export interface UserEdge { node: User; cursor: String; } /* * A connection to a list of items. */ export interface UserConnection { pageInfo: PageInfo; edges: UserEdge[]; aggregate: AggregateUser; } export interface UserSubscriptionPayload { mutation: MutationType; node?: User; updatedFields?: String[]; previousValues?: UserPreviousValues; } export interface AggregateUser { count: Int; } export interface BatchPayload { count: Long; } export type DateTime = Date | string; /* The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `"4"`) or integer (such as `4`) input value will be accepted as an ID. */ export type ID_Input = string | number; export type ID_Output = string; /* The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1. */ export type Int = number; /* The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text. */ export type String = string; /* The `Boolean` scalar type represents `true` or `false`. */ export type Boolean = boolean; /* The `Long` scalar type represents non-fractional signed whole numeric values. Long can represent values between -(2^63) and 2^63 - 1. */ export type Long = string; ================================================ FILE: examples/with-prisma/package.json ================================================ { "name": "graphql-authentication-with-prisma-example", "private": true, "version": "1.0.0", "license": "ISC", "dependencies": { "email-templates": "^4.0.1", "graphql-authentication": "^0.5.3", "graphql-authentication-prisma": "^0.1.3", "graphql-cli": "^2.16.4", "graphql-yoga": "^1.14.12", "prisma": "^1.11.1", "prisma-binding": "^2.1.0", "ts-node": "^7.0.0" }, "scripts": { "start": "ts-node server.ts", "prisma": "PRISMA_MANAGEMENT_API_SECRET=mymanagementsecret123 prisma" } } ================================================ FILE: examples/with-prisma/prisma.yml ================================================ endpoint: http://localhost:4466 datamodel: datamodel.graphql hooks: post-deploy: - graphql codegen ================================================ FILE: examples/with-prisma/schema.graphql ================================================ # import Query.*, Mutation.* from "node_modules/graphql-authentication/schema.graphql" type Query { timeline: [Post!]! } type Post { name: String! } ================================================ FILE: examples/with-prisma/server.ts ================================================ import { GraphQLServer } from 'graphql-yoga'; import * as path from 'path'; import * as Email from 'email-templates'; import { Prisma } from './generated/prisma'; import { authQueries, authMutations, graphqlAuthenticationConfig } from 'graphql-authentication'; import { GraphqlAuthenticationPrismaAdapter } from 'graphql-authentication-prisma'; const resolvers = { Query: { ...authQueries, timeline() { return [{ name: 'Testje' }]; } }, Mutation: { ...authMutations } }; const mailer = new Email({ message: { from: 'info@example.com' }, views: { root: path.join(__dirname, 'emails') } }); const server = new GraphQLServer({ typeDefs: './schema.graphql', resolvers, context: req => ({ ...req, db: new Prisma({ endpoint: 'http://localhost:4466', debug: true }), graphqlAuthentication: graphqlAuthenticationConfig({ adapter: new GraphqlAuthenticationPrismaAdapter(), secret: 'wherearemyshoes', mailer, mailAppUrl: 'http://example.com' }) }) }); server.start(() => console.log('Server is running on http://localhost:4000')); ================================================ FILE: examples/with-prisma/utils.ts ================================================ import { Prisma } from './generated/prisma'; export interface Context { db: Prisma; request: any; } ================================================ FILE: examples/with-sequelize/README.md ================================================ # Sequelize example This is an example of how to use GraphQL Authentication with [Sequelize](http://docs.sequelizejs.com/). There is no adapter package for Sequelize yet, so in this example we write our own adapter (see `SequelizeAdapter.js`). Sequelize is configured with SQLite, but only because it doesn't require you to manually start a database so this example stays simple. It also works with MySQL, Postgres etc. ## Usage ``` npm i npm start ``` Go to http://localhost:4000 in your browser and start running some queries and mutations! ================================================ FILE: examples/with-sequelize/SequelizeAdapter.js ================================================ const { User } = require('./database'); // There currently is no package for a sequelize adapter, so we create one ourselves! class GraphqlAuthenticationSequelizeAdapter { findUserById(ctx, id, info) { return User.findById(id); } findUserByEmail(ctx, email, info) { return User.findOne({ where: { email } }); } async userExistsByEmail(ctx, email) { const user = await User.count({ where: { email } }); return user > 0; } // the _createUser and _updateUser methods are just helper methods, they are not used by graphql-authentication. _createUser(ctx, data) { return User.create(data); } _updateUser(ctx, userId, data) { return User.update(data, { where: { id: userId } }); } createUserBySignup(ctx, data) { return this._createUser(ctx, data); } createUserByInvite(ctx, data) { return this._createUser(ctx, data); } updateUserConfirmToken(ctx, userId, data) { return this._updateUser(ctx, userId, data); } updateUserLastLogin(ctx, userId, data) { return this._updateUser(ctx, userId, data); } updateUserPassword(ctx, userId, data) { return this._updateUser(ctx, userId, data); } updateUserResetToken(ctx, userId, data) { return this._updateUser(ctx, userId, data); } updateUserInfo(ctx, userId, data) { return this._updateUser(ctx, userId, data); } updateUserCompleteInvite(ctx, userId, data) { return this._updateUser(ctx, userId, data); } } module.exports = { GraphqlAuthenticationSequelizeAdapter }; ================================================ FILE: examples/with-sequelize/database.js ================================================ const Sequelize = require('sequelize'); const sequelize = new Sequelize({ dialect: 'sqlite' }); const User = sequelize.define('user', { email: Sequelize.STRING, password: Sequelize.STRING, name: Sequelize.STRING, inviteToken: Sequelize.STRING, inviteAccepted: Sequelize.BOOLEAN, emailConfirmed: Sequelize.BOOLEAN, emailConfirmToken: Sequelize.STRING, resetToken: Sequelize.STRING, resetExpires: Sequelize.DATE, deletedAt: Sequelize.DATE, lastLogin: Sequelize.DATE, joinedAt: Sequelize.DATE, isSuper: Sequelize.BOOLEAN }); sequelize.sync(); module.exports = { User }; ================================================ FILE: examples/with-sequelize/emails/inviteUser/html.pug ================================================ p Hi, p You are invited to join *your project*! p a(href=mailAppUrl + '/register/' + email + '/' + inviteToken) Accept invite. ================================================ FILE: examples/with-sequelize/emails/inviteUser/subject.pug ================================================ = `Invited for *your project*!` ================================================ FILE: examples/with-sequelize/emails/passwordReset/html.pug ================================================ p Hi, p You requested a password reset on *your project*. p a(href=mailAppUrl + '/login/reset-password/' + email + '/' + resetToken) Reset my password. ================================================ FILE: examples/with-sequelize/emails/passwordReset/subject.pug ================================================ = `Confirm your email on *your project*` ================================================ FILE: examples/with-sequelize/emails/signupUser/html.pug ================================================ p Hi, p You have just created an account on *your project*! As a last step, please confirm your email: p a(href=mailAppUrl + '/confirm-email/' + email + '/' + emailConfirmToken) Confirm email. ================================================ FILE: examples/with-sequelize/emails/signupUser/subject.pug ================================================ = `Confirm your email on *your project*` ================================================ FILE: examples/with-sequelize/package.json ================================================ { "name": "graphql-authentication-with-sequelize-example", "private": true, "version": "1.0.0", "license": "ISC", "dependencies": { "email-templates": "^4.0.1", "graphql-authentication": "^0.5.3", "graphql-yoga": "^1.14.12", "sequelize": "^4.38.0", "sqlite3": "^4.0.2" }, "scripts": { "start": "node server.js" } } ================================================ FILE: examples/with-sequelize/schema.graphql ================================================ # import Query.*, Mutation.* from "node_modules/graphql-authentication/schema.graphql" type Query { timeline: [Post!]! } type Post { name: String! } ================================================ FILE: examples/with-sequelize/server.js ================================================ const { GraphQLServer } = require('graphql-yoga'); const path = require('path'); const Email = require('email-templates'); const { authQueries, authMutations, graphqlAuthenticationConfig } = require('graphql-authentication'); const { GraphqlAuthenticationSequelizeAdapter } = require('./SequelizeAdapter'); const resolvers = { Query: { ...authQueries, timeline() { return [{ name: 'Testje' }]; } }, Mutation: { ...authMutations } }; const mailer = new Email({ message: { from: 'info@example.com' }, views: { root: path.join(__dirname, 'emails') } }); const server = new GraphQLServer({ typeDefs: './schema.graphql', resolvers, context: req => ({ ...req, graphqlAuthentication: graphqlAuthenticationConfig({ adapter: new GraphqlAuthenticationSequelizeAdapter(), secret: 'wherearemyshoes', mailer, mailAppUrl: 'http://example.com' }) }) }); server.start(() => console.log('Server is running on http://localhost:4000')); ================================================ FILE: examples/with-typeorm/README.md ================================================ # TypeORM example This is an example of how to use GraphQL Authentication with [TypeORM](http://typeorm.io/). There is no adapter package for TypeORM yet, so in this example we write our own adapter (see `TypeOrmAdapter.js`). TypeORM is configured with SQLite, but only because it doesn't require you to manually start a database so this example stays simple. It also works with MySQL, Postgres etc. ## Usage ``` npm i npm start ``` Go to http://localhost:4000 in your browser and start running some queries and mutations! ================================================ FILE: examples/with-typeorm/TypeOrmAdapter.ts ================================================ import { getRepository } from 'typeorm'; import { User } from './database'; import { GraphqlAuthenticationAdapter } from './node_modules/graphql-authentication'; // There currently is no package for a TypeORM adapter, so we create one ourselves! export class GraphqlAuthenticationTypeOrmAdapter implements GraphqlAuthenticationAdapter { private db() { return getRepository(User); } async findUserById(ctx: object, id, info) { return (await this.db().findOne(id)) || null; } async findUserByEmail(ctx: object, email, info) { return (await this.db().findOne({ where: { email } })) || null; } async userExistsByEmail(ctx: object, email) { const user = await this.db().count({ email }); return user > 0; } // the _createUser and _updateUser methods are just helper methods, they are not used by graphql-authentication. async _createUser(ctx: object, data) { const user = ((await this.db().create(data)) as any) as User; await this.db().save(user); return user; } async _updateUser(ctx: object, userId, data) { return await this.db().update(userId, { name: 'kees' })[0]; } createUserBySignup(ctx: object, data) { return this._createUser(ctx, data); } createUserByInvite(ctx: object, data) { return this._createUser(ctx, data); } updateUserConfirmToken(ctx: object, userId, data) { return this._updateUser(ctx, userId, data); } updateUserLastLogin(ctx: object, userId, data) { return this._updateUser(ctx, userId, data); } updateUserPassword(ctx: object, userId, data) { return this._updateUser(ctx, userId, data); } updateUserResetToken(ctx: object, userId, data) { return this._updateUser(ctx, userId, data); } updateUserInfo(ctx: object, userId, data) { return this._updateUser(ctx, userId, data); } updateUserCompleteInvite(ctx: object, userId, data) { return this._updateUser(ctx, userId, data); } } ================================================ FILE: examples/with-typeorm/database.ts ================================================ import 'reflect-metadata'; import { createConnection } from 'typeorm'; export { User } from './entities/User'; createConnection(); ================================================ FILE: examples/with-typeorm/emails/inviteUser/html.pug ================================================ p Hi, p You are invited to join *your project*! p a(href=mailAppUrl + '/register/' + email + '/' + inviteToken) Accept invite. ================================================ FILE: examples/with-typeorm/emails/inviteUser/subject.pug ================================================ = `Invited for *your project*!` ================================================ FILE: examples/with-typeorm/emails/passwordReset/html.pug ================================================ p Hi, p You requested a password reset on *your project*. p a(href=mailAppUrl + '/login/reset-password/' + email + '/' + resetToken) Reset my password. ================================================ FILE: examples/with-typeorm/emails/passwordReset/subject.pug ================================================ = `Confirm your email on *your project*` ================================================ FILE: examples/with-typeorm/emails/signupUser/html.pug ================================================ p Hi, p You have just created an account on *your project*! As a last step, please confirm your email: p a(href=mailAppUrl + '/confirm-email/' + email + '/' + emailConfirmToken) Confirm email. ================================================ FILE: examples/with-typeorm/emails/signupUser/subject.pug ================================================ = `Confirm your email on *your project*` ================================================ FILE: examples/with-typeorm/entities/User.ts ================================================ import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity() export class User { @PrimaryGeneratedColumn() id!: string; @Column() email!: string; @Column() password!: string; @Column() name!: string; @Column({ nullable: true }) inviteToken!: string; @Column({ default: true }) inviteAccepted!: boolean; @Column({ default: true }) emailConfirmed!: boolean; @Column({ nullable: true }) emailConfirmToken!: string; @Column({ nullable: true }) resetToken!: string; @Column({ nullable: true }) resetExpires!: Date; @Column({ nullable: true }) deletedAt!: Date; @Column({ nullable: true }) lastLogin!: Date; @Column() joinedAt!: Date; @Column({ default: false }) isSuper!: boolean; } ================================================ FILE: examples/with-typeorm/ormconfig.json ================================================ { "type": "sqlite", "database": ":memory:", "synchronize": true, "logging": true, "entities": ["entities/**/*.ts"] } ================================================ FILE: examples/with-typeorm/package.json ================================================ { "name": "graphql-authentication-with-typeorm-example", "private": true, "version": "1.0.0", "license": "ISC", "dependencies": { "email-templates": "^4.0.1", "graphql-authentication": "^0.5.4", "graphql-yoga": "^1.14.12", "reflect-metadata": "^0.1.12", "sqlite3": "^4.0.2", "typeorm": "^0.2.7" }, "scripts": { "start": "ts-node server.ts" }, "devDependencies": { "ts-node": "^7.0.0", "typescript": "^2.9.2" } } ================================================ FILE: examples/with-typeorm/schema.graphql ================================================ # import Query.*, Mutation.* from "node_modules/graphql-authentication/schema.graphql" type Query { timeline: [Post!]! } type Post { name: String! } ================================================ FILE: examples/with-typeorm/server.ts ================================================ import { GraphQLServer } from 'graphql-yoga'; import * as Email from 'email-templates'; import { authQueries, authMutations, graphqlAuthenticationConfig } from 'graphql-authentication'; import { GraphqlAuthenticationTypeOrmAdapter } from './TypeOrmAdapter'; const resolvers = { Query: { ...authQueries, timeline() { return [{ name: 'Testje' }]; } }, Mutation: { ...authMutations } }; const mailer = new Email({ message: { from: 'info@example.com' } }); const server = new GraphQLServer({ typeDefs: './schema.graphql', resolvers, context: req => ({ ...req, graphqlAuthentication: graphqlAuthenticationConfig({ adapter: new GraphqlAuthenticationTypeOrmAdapter(), secret: 'wherearemyshoes', mailer, mailAppUrl: 'http://example.com' }) }) }); server.start(() => console.log('Server is running on http://localhost:4000')); ================================================ FILE: examples/with-typeorm/tsconfig.json ================================================ { "compilerOptions": { "target": "ES2017", "module": "commonjs", "experimentalDecorators": true, "emitDecoratorMetadata": true, "lib": ["esnext"], "strict": true, "strictFunctionTypes": false, "noImplicitAny": false, "forceConsistentCasingInFileNames": true } } ================================================ FILE: lerna.json ================================================ { "lerna": "2.11.0", "packages": [ "packages/*" ], "version": "independent", "npmClient": "yarn", "useWorkspaces": true } ================================================ FILE: live-demo/InMemoryAdapter.js ================================================ class GraphqlAuthenticationInMemoryAdapter { constructor() { this.users = []; } // If you'd use a database you wouldn't need this _generateId() { const lastUser = this.users[this.users.length - 1]; if (lastUser) { return String(parseInt(lastUser.id) + 1); } return '1'; } findUserById(ctx, id, info) { return Promise.resolve(this.users.find(user => user.id === id) || null); } findUserByEmail(ctx, email, info) { return Promise.resolve( this.users.find(user => user.email === email) || null ); } async userExistsByEmail(ctx, email) { return Promise.resolve(this.users.some(user => user.email === email)); } _createUser(ctx, data) { const user = { id: this._generateId(), ...data }; this.users.push(user); return Promise.resolve(user); } async _updateUser(ctx, userId, data) { const user = await this.findUserById(ctx, userId); Object.assign(user, data); return Promise.resolve(user); } createUserBySignup(ctx, data) { return this._createUser(ctx, data); } createUserByInvite(ctx, data) { return this._createUser(ctx, data); } updateUserConfirmToken(ctx, userId, data) { return this._updateUser(ctx, userId, data); } updateUserLastLogin(ctx, userId, data) { return this._updateUser(ctx, userId, data); } updateUserPassword(ctx, userId, data) { return this._updateUser(ctx, userId, data); } updateUserResetToken(ctx, userId, data) { return this._updateUser(ctx, userId, data); } updateUserInfo(ctx, userId, data) { return this._updateUser(ctx, userId, data); } updateUserCompleteInvite(ctx, userId, data) { return this._updateUser(ctx, userId, data); } } module.exports = { GraphqlAuthenticationInMemoryAdapter }; ================================================ FILE: live-demo/README.md ================================================ # Live Demo The code in this repository is used to host a live demo on x.now.sh (TODO). The live demo uses an in-memory adapter, so everytime the server restarts the data is gone. For a demo this is perfect :). ## Test locally Run `yarn && yarn start`. ## Deploy on Now Copy `.env.example` to `.env` and fill the variables in. ``` npm i -g now now --dotenv now alias graphql-authentication-demo.now.sh ``` ================================================ FILE: live-demo/email.js ================================================ const { createTransport } = require('nodemailer'); const mailgun = require('nodemailer-mailgun-transport'); const Email = require('email-templates'); const apiKey = process.env.MAILGUN_API_KEY; const mailgunConfig = { auth: { api_key: apiKey, domain: process.env.MAILGUN_DOMAIN } }; // Just send the email locally if apikey is not filled in const transporter = apiKey ? createTransport(mailgun(mailgunConfig)) : undefined; email = new Email({ message: { from: process.env.MAIL_FROM }, send: true, transport: transporter }); module.exports = { email }; ================================================ FILE: live-demo/emails/inviteUser/html.pug ================================================ p Hi, p You are invited to join GraphQL Authentication Demo! Accept the invite by writing this mutation: pre | mutation { | signupByInvite( | data: { | name: "Enter your name here", | password: "myverystrongpassword", | email: "#{email}", | inviteToken: "#{inviteToken}" | } | ) { | token | } | } p em In a real-world application the user wouldn't have to run the mutation itself of course, it would something like this:
a(href=mailAppUrl + '/register/' + email + '/' + inviteToken) Accept invite. ================================================ FILE: live-demo/emails/inviteUser/subject.pug ================================================ = `Invited for GraphQL Authentication Demo!` ================================================ FILE: live-demo/emails/passwordReset/html.pug ================================================ p Hi, p You requested a password reset on GraphQL Authentication Demo. Reset your password by writing this mutation: pre | mutation { | passwordReset(password: "mynewpassword", email: "#{email}", resetToken: "#{resetToken}") { | id | } |} p em In a real-world application the user wouldn't have to run the mutation itself of course, it would something like this:
a(href=mailAppUrl + '/login/reset-password/' + email + '/' + resetToken) Reset my password. ================================================ FILE: live-demo/emails/passwordReset/subject.pug ================================================ = `Confirm your email on GraphQL Authentication Demo` ================================================ FILE: live-demo/emails/signupUser/html.pug ================================================ p Hi, p You have just created an account on GraphQL Authentication Demo! As a last step, please confirm your email by writing this mutation: pre | mutation { | confirmEmail(email: "#{email}", emailConfirmToken: "#{emailConfirmToken}") { | token | } | } p em In a real-world application the user wouldn't have to run the mutation itself of course, it would something like this:
a(href=mailAppUrl + '/confirm-email/' + email + '/' + emailConfirmToken) Confirm your email. ================================================ FILE: live-demo/emails/signupUser/subject.pug ================================================ = `Confirm your email on GraphQL Authentication Demo` ================================================ FILE: live-demo/index.js ================================================ const { GraphQLServer } = require('graphql-yoga'); const { authQueries, authMutations, graphqlAuthenticationConfig } = require('graphql-authentication'); const { GraphqlAuthenticationInMemoryAdapter } = require('./InMemoryAdapter'); const { email } = require('./email'); const adapter = new GraphqlAuthenticationInMemoryAdapter(); const resolvers = { Query: { ...authQueries }, Mutation: { ...authMutations } }; const server = new GraphQLServer({ typeDefs: './schema.graphql', resolvers, context: req => ({ ...req, graphqlAuthentication: graphqlAuthenticationConfig({ adapter, secret: 'wherearemyshoes', mailer: email, mailAppUrl: 'http://example.com' }) }) }); server.start(() => console.log('Server is running on http://localhost:4000')); ================================================ FILE: live-demo/package.json ================================================ { "name": "graphql-authentication-live-demo", "private": true, "version": "1.0.0", "license": "ISC", "dependencies": { "email-templates": "^4.0.1", "graphql": "^0.13.2", "graphql-authentication": "^0.5.4", "graphql-yoga": "^1.14.12", "nodemailer-mailgun-transport": "^1.4.0", "now": "^11.2.10" }, "scripts": { "start": "node index.js" }, "now": { "alias": [ "graphql-authentication-demo.now.sh" ] } } ================================================ FILE: live-demo/schema.graphql ================================================ # import Query.*, Mutation.* from "node_modules/graphql-authentication/schema.graphql" type Query ================================================ FILE: package.json ================================================ { "private": true, "workspaces": [ "packages/*" ], "devDependencies": { "husky": "^0.14.3", "lerna": "^2.11.0", "prettier": "^1.13.6", "pretty-quick": "^1.6.0" }, "scripts": { "publish": "lerna publish", "precommit": "pretty-quick --staged" } } ================================================ FILE: packages/graphql-authentication/README.md ================================================ # GraphQL Authentication A very opinionated user authentication package for [GraphQL](https://graphql.org/). It uses old-school email/password authentication. This package does not access your data layer (e.g. an ORM); for that you need to write a _adapter_ (which is not hard to do). If you use Prisma, there is already an adapter for you, **[graphql-authentication-prisma](https://github.com/Volst/graphql-authentication/tree/master/packages/graphql-authentication-prisma)**. ### [Full Documentation](https://github.com/Volst/graphql-authentication/blob/master/README.md) ================================================ FILE: packages/graphql-authentication/package.json ================================================ { "name": "graphql-authentication", "version": "0.5.5", "description": "Makes it super easy to do do boring authentication stuff with GraphQL (login, password reset, ...)", "author": "kees@volst.nl", "repository": "Volst/graphql-authentication", "keywords": [ "graphql", "user", "authentication", "login" ], "license": "ISC", "private": false, "main": "dist/index.js", "typings": "dist/index.d.ts", "engines": { "node": ">=8.0" }, "files": [ "dist", "schema.graphql" ], "scripts": { "build": "rm -rf dist && tsc -p tsconfig.build.json", "lint": "tslint -p .", "prepublishOnly": "npm run -s build", "test": "jest --watch", "test-coverage": "jest --coverage", "ci": "npm run -s lint && npm run -s build && npm run -s test-coverage && codecov", "graphql-types": "graphql-binding --input src/schema.ts --language typescript --outputBinding src/binding.ts" }, "devDependencies": { "@types/email-templates": "^3.5.0", "@types/jest": "^23.1.0", "@volst/tslint-config": "^0.2.1", "codecov": "^3.0.2", "email-templates": "^4.0.1", "graphql-binding": "^2.1.1", "graphql-cli": "^2.16.3", "graphql-request": "^1.6.0", "graphql-yoga": "1.14.10", "jest": "^23.1.0", "nodemon": "^1.17.3", "pug": "^2.0.3", "ts-jest": "^22.4.6", "ts-node": "^7.0.0", "tslint": "^5.9.1", "typescript": "^2.8.3" }, "dependencies": { "@types/bcryptjs": "^2.4.1", "@types/jsonwebtoken": "^7.2.6", "@types/uuid": "^3.4.3", "@types/validator": "^9.4.1", "apollo-errors": "^1.9.0", "bcryptjs": "^2.4.3", "jsonwebtoken": "^8.2.1", "uuid": "^3.2.1", "validator": "^10.2.0" }, "peerDependencies": { "email-templates": "^3.6 || ^4" }, "jest": { "roots": [ "./src" ], "transform": { "^.+\\.ts$": "ts-jest" }, "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(js|ts)$", "setupTestFrameworkScriptFile": "/src/__tests__/setup.ts", "moduleFileExtensions": [ "ts", "js", "json", "node" ] } } ================================================ FILE: packages/graphql-authentication/schema.graphql ================================================ scalar DateTime type Query { currentUser: User } type Mutation { signupByInvite(data: SignupByInviteInput!): AuthPayload! signup(data: SignupInput!): AuthPayload! confirmEmail(email: String!, emailConfirmToken: String!): AuthPayload! inviteUser(data: InviteUserInput!): UserIdPayload! login(email: String!, password: String!): AuthPayload! changePassword(oldPassword: String!, newPassword: String!): UserIdPayload! updateCurrentUser(data: UserUpdateInput!): User triggerPasswordReset(email: String!): TriggerPasswordResetPayload! passwordReset( email: String! resetToken: String! password: String! ): UserIdPayload! } type AuthPayload { token: String! user: User! } type UserIdPayload { id: ID! } type TriggerPasswordResetPayload { ok: Boolean! } type User { id: ID! email: String! name: String! inviteAccepted: Boolean! emailConfirmed: Boolean! deletedAt: DateTime lastLogin: DateTime joinedAt: DateTime! isSuper: Boolean! } input InviteUserInput { email: String! } input UserUpdateInput { email: String name: String } input SignupByInviteInput { email: String! inviteToken: String! password: String! name: String! } input SignupInput { email: String! password: String! name: String! } ================================================ FILE: packages/graphql-authentication/src/Adapter.ts ================================================ import { Context } from './utils'; export type DateTime = Date | string; export type ID = string; export interface User { id: ID; email: string; password: string; name: string; inviteToken?: string; inviteAccepted: Boolean; emailConfirmed: Boolean; emailConfirmToken?: string; resetToken?: string; resetExpires?: DateTime; deletedAt?: DateTime; lastLogin?: DateTime; joinedAt: DateTime; isSuper: Boolean; } export interface GraphqlAuthenticationAdapter { findUserById(ctx: Context, id: ID, info?: any): Promise; findUserByEmail( ctx: Context, email: string, info?: any ): Promise; userExistsByEmail(ctx: Context, email: string): Promise; createUserBySignup(ctx: Context, data: any): Promise; createUserByInvite(ctx: Context, data: any): Promise; updateUserConfirmToken( ctx: Context, userId: ID, data: any ): Promise; updateUserLastLogin( ctx: Context, userId: ID, data: any ): Promise; updateUserPassword(ctx: Context, userId: ID, data: any): Promise; updateUserResetToken( ctx: Context, userId: ID, data: any ): Promise; updateUserInfo(ctx: Context, userId: ID, data: any): Promise; updateUserCompleteInvite( ctx: Context, userId: ID, data: any ): Promise; } ================================================ FILE: packages/graphql-authentication/src/Config.ts ================================================ import * as Email from 'email-templates'; import { User } from './Adapter'; import { Context } from './utils'; import { GraphqlAuthenticationAdapter } from './Adapter'; export interface IGraphqlAuthenticationConfig { mailer?: Email; mailAppUrl?: string; secret: string; requiredConfirmedEmailForLogin?: boolean; hookInviteUserPostCreate?: ( data: any, ctx: Context, user: User ) => Promise; adapter: GraphqlAuthenticationAdapter; validatePassword?: (value: string) => boolean; } export function graphqlAuthenticationConfig( options: IGraphqlAuthenticationConfig ) { const defaults = { requiredConfirmedEmailForLogin: false, validatePassword: value => value.length >= 8 }; if (!options.adapter) { throw new Error( 'You forgot to add the `adapter` option to graphql-authentication!' ); } return Object.assign(defaults, options); } ================================================ FILE: packages/graphql-authentication/src/__tests__/mutations.ts ================================================ import { client, clientWithAuth, startServer, FakeAdapter } from './setup'; test('signup - a new user', async () => { const req = client(await startServer()); const result = await req.request(`mutation { signup(data: {name: "Roger", email: "roger@volst.nl", password: "testtest2"}) { token user { id name } } }`); expect((result as any).signup).toEqual({ // Poorly check for a JWT token token: expect.stringContaining('.'), user: { id: '3', name: 'Roger' } }); }); test('signup - with existent user', async () => { expect.assertions(1); const req = client(await startServer()); try { await req.request(`mutation { signup(data: {name: "Kees", email: "kees@volst.nl", password: "testtest2"}) { token } }`); } catch (e) { expect(String(e)).toMatch(/User already exists with this email/); } }); test('signup - with weak password', async () => { expect.assertions(1); const req = client(await startServer()); try { await req.request(`mutation { signup(data: {name: "Roger", email: "roger@volst.nl", password: "test"}) { token } }`); } catch (e) { expect(String(e)).toMatch(/Password is too short/); } }); test('signup - with custom password validation', async () => { expect.assertions(1); const req = client( await startServer({ graphqlAuthentication: { validatePassword: value => { return value.length > 400; } } }) ); try { await req.request(`mutation { signup(data: {name: "Roger", email: "roger@volst.nl", password: "testtest2"}) { token } }`); } catch (e) { expect(String(e)).toMatch(/Password is too short/); } }); test('login - correct', async () => { const req = client(await startServer()); const result = await req.request(`mutation { login(email: "kees@volst.nl", password: "testtest2") { token user { id name } } }`); expect((result as any).login).toEqual({ // Poorly check for a JWT token token: expect.stringContaining('.'), user: { id: '2', name: 'Kees' } }); }); test('login - non-existent user', async () => { const req = client(await startServer()); expect.assertions(1); try { await req.request(`mutation { login(email: "roger@volst.nl", password: "testtest2") { token } }`); } catch (e) { expect(String(e)).toMatch(/No user found/); } }); test('login - wrong password', async () => { expect.assertions(1); const req = client(await startServer()); try { await req.request(`mutation { login(email: "kees@volst.nl", password: "testtest1") { token } }`); } catch (e) { expect(String(e)).toMatch(/No user found/); } }); test('update current user data - correct', async () => { const req = clientWithAuth(await startServer()); const result = await req.request(`mutation { updateCurrentUser(data: {name: "Voldemort"}) { id name } }`); expect((result as any).updateCurrentUser).toEqual({ id: '2', name: 'Voldemort' }); }); test('update current user data - wrong old passwd', async () => { expect.assertions(1); const req = clientWithAuth(await startServer()); try { await req.request(`mutation { changePassword(oldPassword: "testtest3", newPassword: "testtest4") { id } }`); } catch (e) { expect(String(e)).toMatch(/Invalid old password/); } }); test('update user password', async () => { const req = clientWithAuth(await startServer()); const result = await req.request(`mutation { changePassword(oldPassword: "testtest2", newPassword: "testtest3") { id } }`); expect((result as any).changePassword).toEqual({ id: '2' }); // Now verify the password has actually been changed correctly. const result2 = await req.request(`mutation { login(email: "kees@volst.nl", password: "testtest3") { user { id } } }`); expect((result2 as any).login.user).toEqual({ id: '2' }); }); test('trigger password reset - correct', async () => { expect.assertions(6); const req = clientWithAuth(await startServer()); const spy = jest.spyOn(FakeAdapter.prototype, 'updateUserResetToken'); const result = await req.request(`mutation { triggerPasswordReset(email: "kees@volst.nl") { ok } }`); expect(spy).toHaveBeenCalled(); expect((result as any).triggerPasswordReset).toEqual({ ok: true }); const { resetToken } = await spy.mock.results[0].value; // Verify the resetToken is a UUID expect(resetToken.length).toBe(36); const result2 = await req.request(`mutation { passwordReset(email: "kees@volst.nl", password: "testtest4", resetToken: "${resetToken}") { id } }`); expect((result2 as any).passwordReset).toEqual({ id: '2' }); const result3 = await req.request(`mutation { login(email: "kees@volst.nl", password: "testtest4") { user { id } } }`); expect((result3 as any).login.user).toEqual({ id: '2' }); // Now verify that the resetToken is now invalid try { await req.request(`mutation { passwordReset(email: "kees@volst.nl", password: "badbadbad", resetToken: "${resetToken}") { id } }`); } catch (e) { expect(String(e)).toMatch(/No user found/); } spy.mockRestore(); }); test('invite user - correct', async () => { expect.assertions(6); const req = clientWithAuth(await startServer()); const spy = jest.spyOn(FakeAdapter.prototype, 'createUserByInvite'); const result = await req.request(`mutation { inviteUser(data: {email: "roger@volst.nl"}) { id } }`); expect(spy).toHaveBeenCalled(); expect((result as any).inviteUser).toEqual({ id: '3' }); const { inviteToken } = await spy.mock.results[0].value; // Verify the resetToken is a UUID expect(inviteToken.length).toBe(36); const SIGNUP_INVITE = `mutation { signupByInvite(data:{name: "Roger", email: "roger@volst.nl", password: "testtest4", inviteToken: "${inviteToken}"}) { user { id } } }`; const result2 = await req.request(SIGNUP_INVITE); expect((result2 as any).signupByInvite.user).toEqual({ id: '3' }); const result3 = await req.request(`mutation { login(email: "roger@volst.nl", password: "testtest4") { user { id } } }`); expect((result3 as any).login.user).toEqual({ id: '3' }); // Now verify that the inviteToken is now invalid try { await req.request(SIGNUP_INVITE); } catch (e) { expect(String(e)).toMatch(/inviteToken is invalid/); } spy.mockRestore(); }); test('confirm email - correct', async () => { expect.assertions(6); const req = clientWithAuth(await startServer()); const spy = jest.spyOn(FakeAdapter.prototype, 'createUserBySignup'); const result = await req.request(`mutation { signup(data:{name: "Roger", email: "roger@volst.nl", password: "testtest4"}) { user { id } } }`); expect(spy).toHaveBeenCalled(); expect((result as any).signup.user).toEqual({ id: '3' }); const { emailConfirmToken } = await spy.mock.results[0].value; // Verify the emailConfirmToken is a UUID expect(emailConfirmToken.length).toBe(36); const CONFIRM_EMAIL = `mutation { confirmEmail(email: "roger@volst.nl", emailConfirmToken: "${emailConfirmToken}") { user { id } } }`; const result2 = await req.request(CONFIRM_EMAIL); expect((result2 as any).confirmEmail.user).toEqual({ id: '3' }); const result3 = await req.request(`mutation { login(email: "roger@volst.nl", password: "testtest4") { user { id } } }`); expect((result3 as any).login.user).toEqual({ id: '3' }); // Now verify that the emailConfirmToken is now invalid try { await req.request(CONFIRM_EMAIL); } catch (e) { expect(String(e)).toMatch(/emailConfirmToken is invalid/); } spy.mockRestore(); }); ================================================ FILE: packages/graphql-authentication/src/__tests__/queries.ts ================================================ import { client, startServer, clientWithAuth } from './setup'; test('currentUser - throw error when login fails', async () => { const req = client(await startServer()); expect.assertions(1); try { await req.request(`query { currentUser { name } }`); } catch (e) { expect(String(e)).toMatch(/Not authorized/); } }); test('currentUser - fetch user data', async () => { const req = clientWithAuth(await startServer()); const result = await req.request(`query { currentUser { name } }`); expect((result as any).currentUser.name).toBe('Kees'); }); ================================================ FILE: packages/graphql-authentication/src/__tests__/setup.ts ================================================ import { GraphQLServer } from 'graphql-yoga'; import { GraphQLClient } from 'graphql-request'; import { graphqlAuthenticationConfig, authQueries, authMutations, GraphqlAuthenticationAdapter, User, ID } from '..'; export class FakeAdapter implements GraphqlAuthenticationAdapter { users: User[] = [ { id: '2', name: 'Kees', password: '$2a$10$3dcRen7qMwJmzUzgj7cjUukHYlPTTCAjFhfF00.5WAFhhClTp6H4y', // testtest2 email: 'kees@volst.nl', inviteAccepted: true, emailConfirmed: true, joinedAt: '2018-06-29T14:26:57+00:00', isSuper: false, lastLogin: '' } ]; // If you'd use a database you wouldn't need this _generateId() { const lastUser = this.users[this.users.length - 1]; return String(parseInt(lastUser.id) + 1); } findUserById(ctx: object, id: ID, info?: any) { return Promise.resolve(this.users.find(user => user.id === id) || null); } findUserByEmail(ctx: any, email: string) { return Promise.resolve( this.users.find(user => user.email === email) || null ); } userExistsByEmail(ctx: any, email: string) { return Promise.resolve(this.users.some(user => user.email === email)); } createUserBySignup(ctx: any, data: any) { const user = { id: this._generateId(), ...data }; this.users.push(user); return Promise.resolve(user); } createUserByInvite(ctx: any, data: any) { const user = { id: this._generateId(), ...data }; this.users.push(user); return Promise.resolve(user); } async updateUserLastLogin(ctx: any, userId: string, data: any) { const user = await this.findUserById(ctx, userId); Object.assign(user, data); // iel return Promise.resolve(user); } async updateUserInfo(ctx: any, userId: string, data: any) { const user = await this.findUserById(ctx, userId); Object.assign(user, data); // iel return Promise.resolve(user); } async updateUserPassword(ctx: any, userId: string, data: any) { const user = await this.findUserById(ctx, userId); user!.password = data.password; return Promise.resolve(user); } async updateUserResetToken(ctx: any, userId: string, data: any) { const user = await this.findUserById(ctx, userId); Object.assign(user, data); // iel return Promise.resolve(user); } async updateUserCompleteInvite(ctx: any, userId: string, data: any) { const user = await this.findUserById(ctx, userId); Object.assign(user, data); // iel return Promise.resolve(user); } async updateUserConfirmToken(ctx: any, userId: string, data: any) { const user = await this.findUserById(ctx, userId); Object.assign(user, data); // iel return Promise.resolve(user); } } // In nodejs run `require('jsonwebtoken').sign({ userId: '2' }, 'wherearemyshoes')` const AUTH_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIyIiwiaWF0IjoxNTI5MjUxNjQ4fQ.Tw4a0CI3r_8GmyuO1v2aMonrQtKV9QFYnXoxQz0cyRQ'; let http: any; export async function startServer(options: any = {}) { if (http) { await http.close(); } const adapter = new FakeAdapter() as any; const server = new GraphQLServer({ typeDefs: './schema.graphql', resolvers: { Query: { ...authQueries }, Mutation: { ...authMutations } }, context: req => ({ ...req, graphqlAuthentication: graphqlAuthenticationConfig({ secret: 'wherearemyshoes', adapter, ...options.graphqlAuthentication }) }) }); http = await server.start({ port: 0 }); const { port } = http.address(); return `http://localhost:${port}/`; } afterAll(async () => { if (http) { await http.close(); } }); export const clientWithAuth = uri => new GraphQLClient(uri, { headers: { Authorization: `Bearer ${AUTH_KEY}` } }); export const client = uri => new GraphQLClient(uri); // TODO: this workaround sucks test('asdf', () => undefined); ================================================ FILE: packages/graphql-authentication/src/binding.ts ================================================ import { makeBindingClass, Options } from 'graphql-binding'; import { GraphQLResolveInfo, GraphQLSchema } from 'graphql'; import { IResolvers } from 'graphql-tools/dist/Interfaces'; import schema from './schema'; export interface Query { currentUser: ( args?: {}, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; } export interface Mutation { signupByInvite: ( args: { data: SignupByInviteInput }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; signup: ( args: { data: SignupInput }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; confirmEmail: ( args: { email: String; emailConfirmToken: String }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; inviteUser: ( args: { data: InviteUserInput }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; login: ( args: { email: String; password: String }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; changePassword: ( args: { oldPassword: String; newPassword: String }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; updateCurrentUser: ( args: { data: UserUpdateInput }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; triggerPasswordReset: ( args: { email: String }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; passwordReset: ( args: { email: String; resetToken: String; password: String }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; } export interface Subscription {} export interface Binding { query: Query; mutation: Mutation; subscription: Subscription; request: ( query: string, variables?: { [key: string]: any } ) => Promise; delegate( operation: 'query' | 'mutation', fieldName: string, args: { [key: string]: any; }, infoOrQuery?: GraphQLResolveInfo | string, options?: Options ): Promise; delegateSubscription( fieldName: string, args?: { [key: string]: any; }, infoOrQuery?: GraphQLResolveInfo | string, options?: Options ): Promise>; getAbstractResolvers(filterSchema?: GraphQLSchema | string): IResolvers; } export interface BindingConstructor { new (...args): T; } export const Binding = makeBindingClass>({ schema }); /** * Types */ export interface SignupByInviteInput { email: String; inviteToken: String; password: String; name: String; } export interface SignupInput { email: String; password: String; name: String; } export interface InviteUserInput { email: String; } export interface UserUpdateInput { email?: String; name?: String; } export interface AuthPayload { token: String; user: User; } export interface TriggerPasswordResetPayload { ok: Boolean; } export interface User { id: ID_Output; email: String; name: String; inviteAccepted: Boolean; emailConfirmed: Boolean; deletedAt?: DateTime; lastLogin?: DateTime; joinedAt: DateTime; isSuper: Boolean; } export interface UserIdPayload { id: ID_Output; } /* The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text. */ export type String = string; /* The `Boolean` scalar type represents `true` or `false`. */ export type Boolean = boolean; export type DateTime = Date | string; /* The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `"4"`) or integer (such as `4`) input value will be accepted as an ID. */ export type ID_Input = string | number; export type ID_Output = string; ================================================ FILE: packages/graphql-authentication/src/errors.ts ================================================ import { createError } from 'apollo-errors'; export const MissingDataError = createError('MissingDataError', { message: 'Not all required fields are filled in.' }); export const InvalidEmailError = createError('InvalidEmailError', { message: 'Given email is invalid.' }); export const ResetTokenExpiredError = createError('ResetTokenExpiredError', { message: 'resetToken expired.' }); export const PasswordTooShortError = createError('PasswordTooShortError', { message: 'Password is too short.' }); export const UserNotFoundError = createError('UserNotFoundError', { message: 'No user found.' }); export const InvalidInviteTokenError = createError('InvalidInviteTokenError', { message: 'inviteToken is invalid.' }); export const InvalidEmailConfirmToken = createError( 'InvalidEmailConfirmToken', { message: 'emailConfirmToken is invalid.' } ); export const UserEmailExistsError = createError('UserEmailExistsError', { message: 'User already exists with this email.' }); export const UserInviteNotAcceptedError = createError( 'UserInviteNotAcceptedError', { message: 'User has not accepted invite yet.' } ); export const UserDeletedError = createError('UserDeletedError', { message: 'User has been deleted.' }); export const UserEmailUnconfirmedError = createError( 'UserEmailUnconfirmedError', { message: 'Users email has not been confirmed yet.' } ); export const InvalidOldPasswordError = createError('InvalidOldPasswordError', { message: 'Invalid old password.' }); ================================================ FILE: packages/graphql-authentication/src/index.ts ================================================ export { mutations as authMutations } from './mutations'; export { queries as authQueries } from './queries'; export { getUser, getUserId, isAuthResolver, Context } from './utils'; export { graphqlAuthenticationConfig } from './Config'; export { GraphqlAuthenticationAdapter, ID, DateTime, User } from './Adapter'; ================================================ FILE: packages/graphql-authentication/src/mutations.ts ================================================ import * as bcrypt from 'bcryptjs'; import * as jwt from 'jsonwebtoken'; import * as validator from 'validator'; import { v4 as uuid } from 'uuid'; import { getUser, Context } from './utils'; import { User } from './Adapter'; import { MissingDataError, ResetTokenExpiredError, InvalidEmailError, PasswordTooShortError, UserNotFoundError, InvalidInviteTokenError, UserEmailExistsError, UserInviteNotAcceptedError, UserDeletedError, InvalidOldPasswordError, InvalidEmailConfirmToken, UserEmailUnconfirmedError } from './errors'; import { SignupByInviteInput, SignupInput, InviteUserInput, UserUpdateInput } from './binding'; function generateToken(user: User, ctx: Context) { return jwt.sign({ userId: user.id }, ctx.graphqlAuthentication.secret); } function validatePassword(ctx: Context, value: string) { if (!ctx.graphqlAuthentication.validatePassword!(value)) { throw new PasswordTooShortError(); } } function getHashedPassword(value: string) { return bcrypt.hash(value, 10); } export const mutations = { async signupByInvite( parent: any, { data }: { data: SignupByInviteInput }, ctx: Context ) { // Important first check, because i.e. the `inviteToken` could be an empty string // and in that case the find query beneath would find any user with any given email, // allowing you to change the password of everybody. if (!data.inviteToken || !data.email) { throw new MissingDataError(); } const user = await ctx.graphqlAuthentication.adapter.findUserByEmail( ctx, data.email ); if (!user) { throw new UserNotFoundError(); } if (user.inviteToken !== data.inviteToken || user.inviteAccepted) { throw new InvalidInviteTokenError(); } validatePassword(ctx, data.password); const hashedPassword = await getHashedPassword(data.password); const updatedUser = await ctx.graphqlAuthentication.adapter.updateUserCompleteInvite( ctx, user.id, { name: data.name, inviteToken: '', inviteAccepted: true, password: hashedPassword } ); return { token: generateToken(user, ctx), user: updatedUser }; }, async signup(parent: any, { data }: { data: SignupInput }, ctx: Context) { if (!data.email) { throw new MissingDataError(); } const userExists = await ctx.graphqlAuthentication.adapter.userExistsByEmail( ctx, data.email ); if (userExists) { throw new UserEmailExistsError(); } validatePassword(ctx, data.password); const hashedPassword = await getHashedPassword(data.password); const emailConfirmToken = uuid(); const newUser = await ctx.graphqlAuthentication.adapter.createUserBySignup( ctx, { name: data.name, email: data.email, password: hashedPassword, emailConfirmToken, emailConfirmed: false, inviteAccepted: true, joinedAt: new Date().toISOString() } ); if (ctx.graphqlAuthentication.mailer) { ctx.graphqlAuthentication.mailer.send({ template: 'signupUser', message: { to: newUser.email }, locals: { mailAppUrl: ctx.graphqlAuthentication.mailAppUrl, emailConfirmToken, email: newUser.email } }); } return { token: generateToken(newUser, ctx), user: newUser }; }, async confirmEmail( parent: any, { emailConfirmToken, email }: { emailConfirmToken: string; email: string }, ctx: Context ) { if (!emailConfirmToken || !email) { throw new MissingDataError(); } const user = await ctx.graphqlAuthentication.adapter.findUserByEmail( ctx, email ); if (!user) { throw new UserNotFoundError(); } if (user.emailConfirmToken !== emailConfirmToken || user.emailConfirmed) { throw new InvalidEmailConfirmToken(); } const updatedUser = await ctx.graphqlAuthentication.adapter.updateUserConfirmToken( ctx, user.id, { emailConfirmToken: '', emailConfirmed: true } ); return { token: generateToken(user, ctx), user: updatedUser }; }, async login( parent: any, { email, password }: { email: string; password: string }, ctx: Context ) { const user = await ctx.graphqlAuthentication.adapter.findUserByEmail( ctx, email ); if (!user) { throw new UserNotFoundError(); } if (!user.inviteAccepted) { throw new UserInviteNotAcceptedError(); } if (user.deletedAt) { throw new UserDeletedError(); } if ( ctx.graphqlAuthentication.requiredConfirmedEmailForLogin && !user.emailConfirmed ) { throw new UserEmailUnconfirmedError(); } const valid = await bcrypt.compare(password, user.password); if (!valid) { throw new UserNotFoundError(); } // Purposefully async, this update doesn't matter that much. ctx.graphqlAuthentication.adapter.updateUserLastLogin(ctx, user.id, { lastLogin: new Date().toISOString() }); return { token: generateToken(user, ctx), user }; }, async changePassword( parent: any, { oldPassword, newPassword }: { oldPassword: string; newPassword: string }, ctx: Context ) { const user = await getUser(ctx); const valid = await bcrypt.compare(oldPassword, user.password); if (!valid) { throw new InvalidOldPasswordError(); } validatePassword(ctx, newPassword); const password = await getHashedPassword(newPassword); const newUser = await ctx.graphqlAuthentication.adapter.updateUserPassword( ctx, user.id, { password } ); return { id: newUser!.id }; }, async inviteUser( parent: any, { data }: { data: InviteUserInput }, ctx: Context ) { await getUser(ctx); if (!validator.isEmail(data.email)) { throw new InvalidEmailError(); } const existingUser = await ctx.graphqlAuthentication.adapter.findUserByEmail( ctx, data.email ); if (existingUser) { if (ctx.graphqlAuthentication.hookInviteUserPostCreate) { await ctx.graphqlAuthentication.hookInviteUserPostCreate( data, ctx, existingUser ); } return { id: existingUser.id }; } // This token will be used in the email to the user. // According to https://gist.github.com/joepie91/7105003c3b26e65efcea63f3db82dfba // uuid v4 is safe to be used as random token generator. const inviteToken = uuid(); const newUser = await ctx.graphqlAuthentication.adapter.createUserByInvite( ctx, { email: data.email, inviteToken, inviteAccepted: false, password: '', name: '', joinedAt: new Date().toISOString() } ); if (ctx.graphqlAuthentication.hookInviteUserPostCreate) { await ctx.graphqlAuthentication.hookInviteUserPostCreate( data, ctx, newUser ); } if (ctx.graphqlAuthentication.mailer) { ctx.graphqlAuthentication.mailer.send({ template: 'inviteUser', message: { to: newUser.email }, locals: { mailAppUrl: ctx.graphqlAuthentication.mailAppUrl, inviteToken, email: newUser.email } }); } return { id: newUser.id }; }, async triggerPasswordReset( parent: any, { email }: { email: string }, ctx: Context ) { if (!validator.isEmail(email)) { throw new InvalidEmailError(); } const user = await ctx.graphqlAuthentication.adapter.findUserByEmail( ctx, email ); if (!user) { return { ok: true }; } // This token will be used in the email to the user. // According to https://gist.github.com/joepie91/7105003c3b26e65efcea63f3db82dfba // uuid v4 is safe to be used as random token generator. const resetToken = uuid(); const now = new Date(); // Expires in two hours const resetExpires = new Date(now.getTime() + 7200000).toISOString(); await ctx.graphqlAuthentication.adapter.updateUserResetToken(ctx, user.id, { resetToken, resetExpires }); if (ctx.graphqlAuthentication.mailer) { ctx.graphqlAuthentication.mailer.send({ template: 'passwordReset', message: { to: user.email }, locals: { mailAppUrl: ctx.graphqlAuthentication.mailAppUrl, resetToken, email } }); } return { ok: true }; }, async passwordReset( parent: any, { email, resetToken, password }: { email: string; resetToken: string; password: string }, ctx: Context ) { if (!resetToken || !password) { throw new MissingDataError(); } const user = await ctx.graphqlAuthentication.adapter.findUserByEmail( ctx, email ); if (!user || !user.resetExpires || user.resetToken !== resetToken) { throw new UserNotFoundError(); } if (new Date() > new Date(user.resetExpires)) { throw new ResetTokenExpiredError(); } validatePassword(ctx, password); const hashedPassword = await getHashedPassword(password); await ctx.graphqlAuthentication.adapter.updateUserResetToken(ctx, user.id, { resetToken: '', resetExpires: undefined }); await ctx.graphqlAuthentication.adapter.updateUserPassword(ctx, user.id, { password: hashedPassword }); return { id: user.id }; }, async updateCurrentUser( parent: any, { data }: { data: UserUpdateInput }, ctx: Context ) { const user = await getUser(ctx); await ctx.graphqlAuthentication.adapter.updateUserInfo(ctx, user.id, data); return user; } }; ================================================ FILE: packages/graphql-authentication/src/queries.ts ================================================ import { getUserId, Context } from './utils'; // Without this manual User interface import, TypeScript will create an incorrect queries.d.ts declaration file, WTF? import { User } from './Adapter'; export const queries = { currentUser(parent: any, args: any, ctx: Context, info: any) { const id = getUserId(ctx); return ctx.graphqlAuthentication.adapter.findUserById(ctx, id, info); } }; ================================================ FILE: packages/graphql-authentication/src/schema.ts ================================================ import * as path from 'path'; import { makeExecutableSchema } from 'graphql-tools'; import { importSchema } from 'graphql-import'; // This is only used for generating `src/binding.ts` export default makeExecutableSchema({ typeDefs: importSchema(path.resolve('schema.graphql')) }); ================================================ FILE: packages/graphql-authentication/src/utils.ts ================================================ import * as jwt from 'jsonwebtoken'; import { IGraphqlAuthenticationConfig } from './Config'; import { ID } from './Adapter'; export interface Context { graphqlAuthentication: IGraphqlAuthenticationConfig; request?: any; req?: any; } function _getUserId(ctx: Context): string { // For Apollo Server 2.0+ it is ctx.req and for GraphQL Yoga ctx.request. Maybe there is a better way... const Authorization = (ctx.req || ctx.request).get('Authorization'); if (Authorization) { const token = Authorization.replace('Bearer ', ''); const { userId } = jwt.verify(token, ctx.graphqlAuthentication.secret) as { userId: ID; }; return userId; } return ''; } export function getUserId(ctx: Context): string { const userId = _getUserId(ctx); if (userId) { return userId; } throw new AuthError(); } export function getUser(ctx: Context): Promise { return ctx.graphqlAuthentication.adapter.findUserById(ctx, getUserId(ctx)); } export class AuthError extends Error { constructor() { super('Not authorized'); } } export function isAuthResolver(parent: any, args: any, ctx: Context) { return !!_getUserId(ctx); } ================================================ FILE: packages/graphql-authentication/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "compilerOptions": { "rootDir": "src", "outDir": "dist" }, "include": ["src/index.ts"] } ================================================ FILE: packages/graphql-authentication/tsconfig.json ================================================ { "extends": "../../tsconfig.json" } ================================================ FILE: packages/graphql-authentication-prisma/README.md ================================================ # GraphQL Authentication Prisma A Prisma adapter for [Graphql Authentication](https://github.com/Volst/graphql-authentication/blob/master/README.md). # Install Node v8+ should be used. Install with Yarn or npm: ``` yarn add graphql-authentication graphql-authentication-prisma email-templates npm i graphql-authentication graphql-authentication-prisma email-templates ``` # Usage with Prisma You can read the guide below or checkout [the example](https://github.com/Volst/graphql-authentication/tree/master/examples/with-prisma) to see the full code. ## Step 1 Read the [Usage](https://github.com/Volst/graphql-authentication/blob/master/README.md#usage) section in the full documentation first. ## Step 2 After configuring the basics, you can now add this package as an adapter. Pseudo-code example: ```js import { GraphqlAuthenticationPrismaAdapter } from 'graphql-authentication-prisma'; graphqlAuthentication: graphqlAuthenticationConfig({ adapter: new GraphqlAuthenticationPrismaAdapter({ // Optional, defaults to 'db' prismaContextName: 'db' }) }); ``` ## Step 3 In your Prisma `datamodel.graphql` file, add this [User model](https://github.com/Volst/graphql-authentication/blob/master/examples/with-prisma/datamodel.graphql). Run `prisma deploy` to run the migrations. ### [Full Documentation](https://github.com/Volst/graphql-authentication/blob/master/README.md#documentation) ================================================ FILE: packages/graphql-authentication-prisma/package.json ================================================ { "name": "graphql-authentication-prisma", "version": "0.1.5", "description": "Prisma adapter for graphql-authentication", "author": "kees@volst.nl", "repository": "Volst/graphql-authentication", "keywords": [ "graphql", "user", "authentication", "login", "prisma" ], "license": "ISC", "private": false, "main": "dist/index.js", "typings": "dist/index.d.ts", "engines": { "node": ">=8.0" }, "files": [ "dist" ], "scripts": { "build": "rm -rf dist && tsc -p tsconfig.build.json", "lint": "tslint -p .", "prepublishOnly": "npm run -s build", "test": "jest --watch", "test-coverage": "jest --coverage", "ci": "npm run -s lint && npm run -s build && npm run -s test-coverage && codecov" }, "devDependencies": { "@types/email-templates": "^3.5.0", "@types/jest": "^23.1.0", "@volst/tslint-config": "^0.2.1", "codecov": "^3.0.2", "email-templates": "^4.0.1", "graphql-authentication": "^0.5.5", "graphql-cli": "^2.15.13", "graphql-request": "^1.6.0", "graphql-yoga": "1.14.10", "jest": "^23.1.0", "nodemon": "^1.17.3", "prisma-binding": "^2.0.0", "pug": "^2.0.3", "ts-jest": "^22.4.6", "ts-node": "^7.0.0", "tslint": "^5.9.1", "typescript": "^2.8.3" }, "dependencies": { "@types/bcryptjs": "^2.4.1", "@types/jsonwebtoken": "^7.2.6", "@types/uuid": "^3.4.3", "@types/validator": "^9.4.1", "apollo-errors": "^1.9.0", "bcryptjs": "^2.4.3", "jsonwebtoken": "^8.2.1", "uuid": "^3.2.1", "validator": "^10.2.0" }, "peerDependencies": { "graphql-authentication": "^0.5.0", "prisma-binding": "^2.0.0" }, "jest": { "roots": [ "./src" ], "transform": { "^.+\\.tsx?$": "ts-jest" }, "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", "moduleFileExtensions": [ "ts", "js", "json", "node" ] } } ================================================ FILE: packages/graphql-authentication-prisma/src/Prisma.ts ================================================ import { Prisma, User } from './generated/prisma'; import { GraphqlAuthenticationAdapter, ID } from 'graphql-authentication'; export class GraphqlAuthenticationPrismaAdapter implements GraphqlAuthenticationAdapter { prismaContextName = 'db'; constructor(options: { prismaContextName?: string } = {}) { if (options && options.prismaContextName) { this.prismaContextName = options.prismaContextName; } } private db(ctx: object) { const db: Prisma = ctx[this.prismaContextName]; if (!db) { throw new Error( `The Prisma binding is not attached to the \`${ this.prismaContextName }\` property on your context.` ); } return db; } findUserById(ctx: object, id: ID, info?: any) { return this.db(ctx).query.user({ where: { id } }, info); } findUserByEmail(ctx: object, email: string, info?: any) { return this.db(ctx).query.user( { where: { email: email } }, info ); } userExistsByEmail(ctx: object, email: string) { return this.db(ctx).exists.User({ email }); } private createUser(ctx: object, data: any) { return this.db(ctx).mutation.createUser({ data }); } createUserBySignup(ctx: object, data: any) { return this.createUser(ctx, data); } createUserByInvite(ctx: object, data: any) { return this.createUser(ctx, data); } private updateUser(ctx: object, userId: ID, data: any) { return this.db(ctx).mutation.updateUser({ where: { id: userId }, data }); } updateUserConfirmToken(ctx: object, userId: ID, data: any) { return this.updateUser(ctx, userId, data); } updateUserLastLogin(ctx: object, userId: ID, data: any) { return this.updateUser(ctx, userId, data); } updateUserPassword(ctx: object, userId: ID, data: any) { return this.updateUser(ctx, userId, data); } updateUserResetToken(ctx: object, userId: ID, data: any) { return this.updateUser(ctx, userId, data); } updateUserInfo(ctx: object, userId: ID, data: any) { return this.updateUser(ctx, userId, data); } updateUserCompleteInvite(ctx: object, userId: ID, data: any) { return this.updateUser(ctx, userId, data); } } ================================================ FILE: packages/graphql-authentication-prisma/src/generated/prisma.graphql ================================================ # source: http://localhost:4466 # timestamp: Sun Jun 17 2018 19:34:23 GMT+0200 (CEST) type AggregateUser { count: Int! } type BatchPayload { """ The number of nodes that have been affected by the Batch operation. """ count: Long! } scalar DateTime """ The `Long` scalar type represents non-fractional signed whole numeric values. Long can represent values between -(2^63) and 2^63 - 1. """ scalar Long type Mutation { createUser(data: UserCreateInput!): User! updateUser(data: UserUpdateInput!, where: UserWhereUniqueInput!): User deleteUser(where: UserWhereUniqueInput!): User upsertUser( where: UserWhereUniqueInput! create: UserCreateInput! update: UserUpdateInput! ): User! updateManyUsers(data: UserUpdateInput!, where: UserWhereInput): BatchPayload! deleteManyUsers(where: UserWhereInput): BatchPayload! } enum MutationType { CREATED UPDATED DELETED } """ An object with an ID """ interface Node { """ The id of the object. """ id: ID! } """ Information about pagination in a connection. """ type PageInfo { """ When paginating forwards, are there more items? """ hasNextPage: Boolean! """ When paginating backwards, are there more items? """ hasPreviousPage: Boolean! """ When paginating backwards, the cursor to continue. """ startCursor: String """ When paginating forwards, the cursor to continue. """ endCursor: String } type Query { users( where: UserWhereInput orderBy: UserOrderByInput skip: Int after: String before: String first: Int last: Int ): [User]! user(where: UserWhereUniqueInput!): User usersConnection( where: UserWhereInput orderBy: UserOrderByInput skip: Int after: String before: String first: Int last: Int ): UserConnection! """ Fetches an object given its ID """ node( """ The ID of an object """ id: ID! ): Node } type Subscription { user(where: UserSubscriptionWhereInput): UserSubscriptionPayload } type User implements Node { id: ID! email: String! password: String! name: String! inviteToken: String inviteAccepted: Boolean! emailConfirmed: Boolean! emailConfirmToken: String resetToken: String resetExpires: DateTime deletedAt: DateTime lastLogin: DateTime joinedAt: DateTime! isSuper: Boolean! } """ A connection to a list of items. """ type UserConnection { """ Information to aid in pagination. """ pageInfo: PageInfo! """ A list of edges. """ edges: [UserEdge]! aggregate: AggregateUser! } input UserCreateInput { email: String! password: String! name: String! inviteToken: String inviteAccepted: Boolean emailConfirmed: Boolean emailConfirmToken: String resetToken: String resetExpires: DateTime deletedAt: DateTime lastLogin: DateTime joinedAt: DateTime! isSuper: Boolean } """ An edge in a connection. """ type UserEdge { """ The item at the end of the edge. """ node: User! """ A cursor for use in pagination. """ cursor: String! } enum UserOrderByInput { id_ASC id_DESC email_ASC email_DESC password_ASC password_DESC name_ASC name_DESC inviteToken_ASC inviteToken_DESC inviteAccepted_ASC inviteAccepted_DESC emailConfirmed_ASC emailConfirmed_DESC emailConfirmToken_ASC emailConfirmToken_DESC resetToken_ASC resetToken_DESC resetExpires_ASC resetExpires_DESC deletedAt_ASC deletedAt_DESC lastLogin_ASC lastLogin_DESC joinedAt_ASC joinedAt_DESC isSuper_ASC isSuper_DESC updatedAt_ASC updatedAt_DESC createdAt_ASC createdAt_DESC } type UserPreviousValues { id: ID! email: String! password: String! name: String! inviteToken: String inviteAccepted: Boolean! emailConfirmed: Boolean! emailConfirmToken: String resetToken: String resetExpires: DateTime deletedAt: DateTime lastLogin: DateTime joinedAt: DateTime! isSuper: Boolean! } type UserSubscriptionPayload { mutation: MutationType! node: User updatedFields: [String!] previousValues: UserPreviousValues } input UserSubscriptionWhereInput { """ Logical AND on all given filters. """ AND: [UserSubscriptionWhereInput!] """ Logical OR on all given filters. """ OR: [UserSubscriptionWhereInput!] """ Logical NOT on all given filters combined by AND. """ NOT: [UserSubscriptionWhereInput!] """ The subscription event gets dispatched when it's listed in mutation_in """ mutation_in: [MutationType!] """ The subscription event gets only dispatched when one of the updated fields names is included in this list """ updatedFields_contains: String """ The subscription event gets only dispatched when all of the field names included in this list have been updated """ updatedFields_contains_every: [String!] """ The subscription event gets only dispatched when some of the field names included in this list have been updated """ updatedFields_contains_some: [String!] node: UserWhereInput } input UserUpdateInput { email: String password: String name: String inviteToken: String inviteAccepted: Boolean emailConfirmed: Boolean emailConfirmToken: String resetToken: String resetExpires: DateTime deletedAt: DateTime lastLogin: DateTime joinedAt: DateTime isSuper: Boolean } input UserWhereInput { """ Logical AND on all given filters. """ AND: [UserWhereInput!] """ Logical OR on all given filters. """ OR: [UserWhereInput!] """ Logical NOT on all given filters combined by AND. """ NOT: [UserWhereInput!] id: ID """ All values that are not equal to given value. """ id_not: ID """ All values that are contained in given list. """ id_in: [ID!] """ All values that are not contained in given list. """ id_not_in: [ID!] """ All values less than the given value. """ id_lt: ID """ All values less than or equal the given value. """ id_lte: ID """ All values greater than the given value. """ id_gt: ID """ All values greater than or equal the given value. """ id_gte: ID """ All values containing the given string. """ id_contains: ID """ All values not containing the given string. """ id_not_contains: ID """ All values starting with the given string. """ id_starts_with: ID """ All values not starting with the given string. """ id_not_starts_with: ID """ All values ending with the given string. """ id_ends_with: ID """ All values not ending with the given string. """ id_not_ends_with: ID email: String """ All values that are not equal to given value. """ email_not: String """ All values that are contained in given list. """ email_in: [String!] """ All values that are not contained in given list. """ email_not_in: [String!] """ All values less than the given value. """ email_lt: String """ All values less than or equal the given value. """ email_lte: String """ All values greater than the given value. """ email_gt: String """ All values greater than or equal the given value. """ email_gte: String """ All values containing the given string. """ email_contains: String """ All values not containing the given string. """ email_not_contains: String """ All values starting with the given string. """ email_starts_with: String """ All values not starting with the given string. """ email_not_starts_with: String """ All values ending with the given string. """ email_ends_with: String """ All values not ending with the given string. """ email_not_ends_with: String password: String """ All values that are not equal to given value. """ password_not: String """ All values that are contained in given list. """ password_in: [String!] """ All values that are not contained in given list. """ password_not_in: [String!] """ All values less than the given value. """ password_lt: String """ All values less than or equal the given value. """ password_lte: String """ All values greater than the given value. """ password_gt: String """ All values greater than or equal the given value. """ password_gte: String """ All values containing the given string. """ password_contains: String """ All values not containing the given string. """ password_not_contains: String """ All values starting with the given string. """ password_starts_with: String """ All values not starting with the given string. """ password_not_starts_with: String """ All values ending with the given string. """ password_ends_with: String """ All values not ending with the given string. """ password_not_ends_with: String name: String """ All values that are not equal to given value. """ name_not: String """ All values that are contained in given list. """ name_in: [String!] """ All values that are not contained in given list. """ name_not_in: [String!] """ All values less than the given value. """ name_lt: String """ All values less than or equal the given value. """ name_lte: String """ All values greater than the given value. """ name_gt: String """ All values greater than or equal the given value. """ name_gte: String """ All values containing the given string. """ name_contains: String """ All values not containing the given string. """ name_not_contains: String """ All values starting with the given string. """ name_starts_with: String """ All values not starting with the given string. """ name_not_starts_with: String """ All values ending with the given string. """ name_ends_with: String """ All values not ending with the given string. """ name_not_ends_with: String inviteToken: String """ All values that are not equal to given value. """ inviteToken_not: String """ All values that are contained in given list. """ inviteToken_in: [String!] """ All values that are not contained in given list. """ inviteToken_not_in: [String!] """ All values less than the given value. """ inviteToken_lt: String """ All values less than or equal the given value. """ inviteToken_lte: String """ All values greater than the given value. """ inviteToken_gt: String """ All values greater than or equal the given value. """ inviteToken_gte: String """ All values containing the given string. """ inviteToken_contains: String """ All values not containing the given string. """ inviteToken_not_contains: String """ All values starting with the given string. """ inviteToken_starts_with: String """ All values not starting with the given string. """ inviteToken_not_starts_with: String """ All values ending with the given string. """ inviteToken_ends_with: String """ All values not ending with the given string. """ inviteToken_not_ends_with: String inviteAccepted: Boolean """ All values that are not equal to given value. """ inviteAccepted_not: Boolean emailConfirmed: Boolean """ All values that are not equal to given value. """ emailConfirmed_not: Boolean emailConfirmToken: String """ All values that are not equal to given value. """ emailConfirmToken_not: String """ All values that are contained in given list. """ emailConfirmToken_in: [String!] """ All values that are not contained in given list. """ emailConfirmToken_not_in: [String!] """ All values less than the given value. """ emailConfirmToken_lt: String """ All values less than or equal the given value. """ emailConfirmToken_lte: String """ All values greater than the given value. """ emailConfirmToken_gt: String """ All values greater than or equal the given value. """ emailConfirmToken_gte: String """ All values containing the given string. """ emailConfirmToken_contains: String """ All values not containing the given string. """ emailConfirmToken_not_contains: String """ All values starting with the given string. """ emailConfirmToken_starts_with: String """ All values not starting with the given string. """ emailConfirmToken_not_starts_with: String """ All values ending with the given string. """ emailConfirmToken_ends_with: String """ All values not ending with the given string. """ emailConfirmToken_not_ends_with: String resetToken: String """ All values that are not equal to given value. """ resetToken_not: String """ All values that are contained in given list. """ resetToken_in: [String!] """ All values that are not contained in given list. """ resetToken_not_in: [String!] """ All values less than the given value. """ resetToken_lt: String """ All values less than or equal the given value. """ resetToken_lte: String """ All values greater than the given value. """ resetToken_gt: String """ All values greater than or equal the given value. """ resetToken_gte: String """ All values containing the given string. """ resetToken_contains: String """ All values not containing the given string. """ resetToken_not_contains: String """ All values starting with the given string. """ resetToken_starts_with: String """ All values not starting with the given string. """ resetToken_not_starts_with: String """ All values ending with the given string. """ resetToken_ends_with: String """ All values not ending with the given string. """ resetToken_not_ends_with: String resetExpires: DateTime """ All values that are not equal to given value. """ resetExpires_not: DateTime """ All values that are contained in given list. """ resetExpires_in: [DateTime!] """ All values that are not contained in given list. """ resetExpires_not_in: [DateTime!] """ All values less than the given value. """ resetExpires_lt: DateTime """ All values less than or equal the given value. """ resetExpires_lte: DateTime """ All values greater than the given value. """ resetExpires_gt: DateTime """ All values greater than or equal the given value. """ resetExpires_gte: DateTime deletedAt: DateTime """ All values that are not equal to given value. """ deletedAt_not: DateTime """ All values that are contained in given list. """ deletedAt_in: [DateTime!] """ All values that are not contained in given list. """ deletedAt_not_in: [DateTime!] """ All values less than the given value. """ deletedAt_lt: DateTime """ All values less than or equal the given value. """ deletedAt_lte: DateTime """ All values greater than the given value. """ deletedAt_gt: DateTime """ All values greater than or equal the given value. """ deletedAt_gte: DateTime lastLogin: DateTime """ All values that are not equal to given value. """ lastLogin_not: DateTime """ All values that are contained in given list. """ lastLogin_in: [DateTime!] """ All values that are not contained in given list. """ lastLogin_not_in: [DateTime!] """ All values less than the given value. """ lastLogin_lt: DateTime """ All values less than or equal the given value. """ lastLogin_lte: DateTime """ All values greater than the given value. """ lastLogin_gt: DateTime """ All values greater than or equal the given value. """ lastLogin_gte: DateTime joinedAt: DateTime """ All values that are not equal to given value. """ joinedAt_not: DateTime """ All values that are contained in given list. """ joinedAt_in: [DateTime!] """ All values that are not contained in given list. """ joinedAt_not_in: [DateTime!] """ All values less than the given value. """ joinedAt_lt: DateTime """ All values less than or equal the given value. """ joinedAt_lte: DateTime """ All values greater than the given value. """ joinedAt_gt: DateTime """ All values greater than or equal the given value. """ joinedAt_gte: DateTime isSuper: Boolean """ All values that are not equal to given value. """ isSuper_not: Boolean } input UserWhereUniqueInput { id: ID email: String } ================================================ FILE: packages/graphql-authentication-prisma/src/generated/prisma.ts ================================================ import { GraphQLResolveInfo, GraphQLSchema } from 'graphql'; import { IResolvers } from 'graphql-tools/dist/Interfaces'; import { Options } from 'graphql-binding'; import { makePrismaBindingClass, BasePrismaOptions } from 'prisma-binding'; export interface Query { users: ( args: { where?: UserWhereInput; orderBy?: UserOrderByInput; skip?: Int; after?: String; before?: String; first?: Int; last?: Int; }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; user: ( args: { where: UserWhereUniqueInput }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; usersConnection: ( args: { where?: UserWhereInput; orderBy?: UserOrderByInput; skip?: Int; after?: String; before?: String; first?: Int; last?: Int; }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; node: ( args: { id: ID_Output }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; } export interface Mutation { createUser: ( args: { data: UserCreateInput }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; updateUser: ( args: { data: UserUpdateInput; where: UserWhereUniqueInput }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; deleteUser: ( args: { where: UserWhereUniqueInput }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; upsertUser: ( args: { where: UserWhereUniqueInput; create: UserCreateInput; update: UserUpdateInput; }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; updateManyUsers: ( args: { data: UserUpdateInput; where?: UserWhereInput }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; deleteManyUsers: ( args: { where?: UserWhereInput }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise; } export interface Subscription { user: ( args: { where?: UserSubscriptionWhereInput }, info?: GraphQLResolveInfo | string, options?: Options ) => Promise>; } export interface Exists { User: (where?: UserWhereInput) => Promise; } export interface Prisma { query: Query; mutation: Mutation; subscription: Subscription; exists: Exists; request: ( query: string, variables?: { [key: string]: any } ) => Promise; delegate( operation: 'query' | 'mutation', fieldName: string, args: { [key: string]: any; }, infoOrQuery?: GraphQLResolveInfo | string, options?: Options ): Promise; delegateSubscription( fieldName: string, args?: { [key: string]: any; }, infoOrQuery?: GraphQLResolveInfo | string, options?: Options ): Promise>; getAbstractResolvers(filterSchema?: GraphQLSchema | string): IResolvers; } export interface BindingConstructor { new (options: BasePrismaOptions): T; } /** * Type Defs */ const typeDefs = `type AggregateUser { count: Int! } type BatchPayload { """The number of nodes that have been affected by the Batch operation.""" count: Long! } scalar DateTime """ The \`Long\` scalar type represents non-fractional signed whole numeric values. Long can represent values between -(2^63) and 2^63 - 1. """ scalar Long type Mutation { createUser(data: UserCreateInput!): User! updateUser(data: UserUpdateInput!, where: UserWhereUniqueInput!): User deleteUser(where: UserWhereUniqueInput!): User upsertUser(where: UserWhereUniqueInput!, create: UserCreateInput!, update: UserUpdateInput!): User! updateManyUsers(data: UserUpdateInput!, where: UserWhereInput): BatchPayload! deleteManyUsers(where: UserWhereInput): BatchPayload! } enum MutationType { CREATED UPDATED DELETED } """An object with an ID""" interface Node { """The id of the object.""" id: ID! } """Information about pagination in a connection.""" type PageInfo { """When paginating forwards, are there more items?""" hasNextPage: Boolean! """When paginating backwards, are there more items?""" hasPreviousPage: Boolean! """When paginating backwards, the cursor to continue.""" startCursor: String """When paginating forwards, the cursor to continue.""" endCursor: String } type Query { users(where: UserWhereInput, orderBy: UserOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [User]! user(where: UserWhereUniqueInput!): User usersConnection(where: UserWhereInput, orderBy: UserOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): UserConnection! """Fetches an object given its ID""" node( """The ID of an object""" id: ID! ): Node } type Subscription { user(where: UserSubscriptionWhereInput): UserSubscriptionPayload } type User implements Node { id: ID! email: String! password: String! name: String! inviteToken: String inviteAccepted: Boolean! emailConfirmed: Boolean! emailConfirmToken: String resetToken: String resetExpires: DateTime deletedAt: DateTime lastLogin: DateTime joinedAt: DateTime! isSuper: Boolean! } """A connection to a list of items.""" type UserConnection { """Information to aid in pagination.""" pageInfo: PageInfo! """A list of edges.""" edges: [UserEdge]! aggregate: AggregateUser! } input UserCreateInput { email: String! password: String! name: String! inviteToken: String inviteAccepted: Boolean emailConfirmed: Boolean emailConfirmToken: String resetToken: String resetExpires: DateTime deletedAt: DateTime lastLogin: DateTime joinedAt: DateTime! isSuper: Boolean } """An edge in a connection.""" type UserEdge { """The item at the end of the edge.""" node: User! """A cursor for use in pagination.""" cursor: String! } enum UserOrderByInput { id_ASC id_DESC email_ASC email_DESC password_ASC password_DESC name_ASC name_DESC inviteToken_ASC inviteToken_DESC inviteAccepted_ASC inviteAccepted_DESC emailConfirmed_ASC emailConfirmed_DESC emailConfirmToken_ASC emailConfirmToken_DESC resetToken_ASC resetToken_DESC resetExpires_ASC resetExpires_DESC deletedAt_ASC deletedAt_DESC lastLogin_ASC lastLogin_DESC joinedAt_ASC joinedAt_DESC isSuper_ASC isSuper_DESC updatedAt_ASC updatedAt_DESC createdAt_ASC createdAt_DESC } type UserPreviousValues { id: ID! email: String! password: String! name: String! inviteToken: String inviteAccepted: Boolean! emailConfirmed: Boolean! emailConfirmToken: String resetToken: String resetExpires: DateTime deletedAt: DateTime lastLogin: DateTime joinedAt: DateTime! isSuper: Boolean! } type UserSubscriptionPayload { mutation: MutationType! node: User updatedFields: [String!] previousValues: UserPreviousValues } input UserSubscriptionWhereInput { """Logical AND on all given filters.""" AND: [UserSubscriptionWhereInput!] """Logical OR on all given filters.""" OR: [UserSubscriptionWhereInput!] """Logical NOT on all given filters combined by AND.""" NOT: [UserSubscriptionWhereInput!] """ The subscription event gets dispatched when it's listed in mutation_in """ mutation_in: [MutationType!] """ The subscription event gets only dispatched when one of the updated fields names is included in this list """ updatedFields_contains: String """ The subscription event gets only dispatched when all of the field names included in this list have been updated """ updatedFields_contains_every: [String!] """ The subscription event gets only dispatched when some of the field names included in this list have been updated """ updatedFields_contains_some: [String!] node: UserWhereInput } input UserUpdateInput { email: String password: String name: String inviteToken: String inviteAccepted: Boolean emailConfirmed: Boolean emailConfirmToken: String resetToken: String resetExpires: DateTime deletedAt: DateTime lastLogin: DateTime joinedAt: DateTime isSuper: Boolean } input UserWhereInput { """Logical AND on all given filters.""" AND: [UserWhereInput!] """Logical OR on all given filters.""" OR: [UserWhereInput!] """Logical NOT on all given filters combined by AND.""" NOT: [UserWhereInput!] id: ID """All values that are not equal to given value.""" id_not: ID """All values that are contained in given list.""" id_in: [ID!] """All values that are not contained in given list.""" id_not_in: [ID!] """All values less than the given value.""" id_lt: ID """All values less than or equal the given value.""" id_lte: ID """All values greater than the given value.""" id_gt: ID """All values greater than or equal the given value.""" id_gte: ID """All values containing the given string.""" id_contains: ID """All values not containing the given string.""" id_not_contains: ID """All values starting with the given string.""" id_starts_with: ID """All values not starting with the given string.""" id_not_starts_with: ID """All values ending with the given string.""" id_ends_with: ID """All values not ending with the given string.""" id_not_ends_with: ID email: String """All values that are not equal to given value.""" email_not: String """All values that are contained in given list.""" email_in: [String!] """All values that are not contained in given list.""" email_not_in: [String!] """All values less than the given value.""" email_lt: String """All values less than or equal the given value.""" email_lte: String """All values greater than the given value.""" email_gt: String """All values greater than or equal the given value.""" email_gte: String """All values containing the given string.""" email_contains: String """All values not containing the given string.""" email_not_contains: String """All values starting with the given string.""" email_starts_with: String """All values not starting with the given string.""" email_not_starts_with: String """All values ending with the given string.""" email_ends_with: String """All values not ending with the given string.""" email_not_ends_with: String password: String """All values that are not equal to given value.""" password_not: String """All values that are contained in given list.""" password_in: [String!] """All values that are not contained in given list.""" password_not_in: [String!] """All values less than the given value.""" password_lt: String """All values less than or equal the given value.""" password_lte: String """All values greater than the given value.""" password_gt: String """All values greater than or equal the given value.""" password_gte: String """All values containing the given string.""" password_contains: String """All values not containing the given string.""" password_not_contains: String """All values starting with the given string.""" password_starts_with: String """All values not starting with the given string.""" password_not_starts_with: String """All values ending with the given string.""" password_ends_with: String """All values not ending with the given string.""" password_not_ends_with: String name: String """All values that are not equal to given value.""" name_not: String """All values that are contained in given list.""" name_in: [String!] """All values that are not contained in given list.""" name_not_in: [String!] """All values less than the given value.""" name_lt: String """All values less than or equal the given value.""" name_lte: String """All values greater than the given value.""" name_gt: String """All values greater than or equal the given value.""" name_gte: String """All values containing the given string.""" name_contains: String """All values not containing the given string.""" name_not_contains: String """All values starting with the given string.""" name_starts_with: String """All values not starting with the given string.""" name_not_starts_with: String """All values ending with the given string.""" name_ends_with: String """All values not ending with the given string.""" name_not_ends_with: String inviteToken: String """All values that are not equal to given value.""" inviteToken_not: String """All values that are contained in given list.""" inviteToken_in: [String!] """All values that are not contained in given list.""" inviteToken_not_in: [String!] """All values less than the given value.""" inviteToken_lt: String """All values less than or equal the given value.""" inviteToken_lte: String """All values greater than the given value.""" inviteToken_gt: String """All values greater than or equal the given value.""" inviteToken_gte: String """All values containing the given string.""" inviteToken_contains: String """All values not containing the given string.""" inviteToken_not_contains: String """All values starting with the given string.""" inviteToken_starts_with: String """All values not starting with the given string.""" inviteToken_not_starts_with: String """All values ending with the given string.""" inviteToken_ends_with: String """All values not ending with the given string.""" inviteToken_not_ends_with: String inviteAccepted: Boolean """All values that are not equal to given value.""" inviteAccepted_not: Boolean emailConfirmed: Boolean """All values that are not equal to given value.""" emailConfirmed_not: Boolean emailConfirmToken: String """All values that are not equal to given value.""" emailConfirmToken_not: String """All values that are contained in given list.""" emailConfirmToken_in: [String!] """All values that are not contained in given list.""" emailConfirmToken_not_in: [String!] """All values less than the given value.""" emailConfirmToken_lt: String """All values less than or equal the given value.""" emailConfirmToken_lte: String """All values greater than the given value.""" emailConfirmToken_gt: String """All values greater than or equal the given value.""" emailConfirmToken_gte: String """All values containing the given string.""" emailConfirmToken_contains: String """All values not containing the given string.""" emailConfirmToken_not_contains: String """All values starting with the given string.""" emailConfirmToken_starts_with: String """All values not starting with the given string.""" emailConfirmToken_not_starts_with: String """All values ending with the given string.""" emailConfirmToken_ends_with: String """All values not ending with the given string.""" emailConfirmToken_not_ends_with: String resetToken: String """All values that are not equal to given value.""" resetToken_not: String """All values that are contained in given list.""" resetToken_in: [String!] """All values that are not contained in given list.""" resetToken_not_in: [String!] """All values less than the given value.""" resetToken_lt: String """All values less than or equal the given value.""" resetToken_lte: String """All values greater than the given value.""" resetToken_gt: String """All values greater than or equal the given value.""" resetToken_gte: String """All values containing the given string.""" resetToken_contains: String """All values not containing the given string.""" resetToken_not_contains: String """All values starting with the given string.""" resetToken_starts_with: String """All values not starting with the given string.""" resetToken_not_starts_with: String """All values ending with the given string.""" resetToken_ends_with: String """All values not ending with the given string.""" resetToken_not_ends_with: String resetExpires: DateTime """All values that are not equal to given value.""" resetExpires_not: DateTime """All values that are contained in given list.""" resetExpires_in: [DateTime!] """All values that are not contained in given list.""" resetExpires_not_in: [DateTime!] """All values less than the given value.""" resetExpires_lt: DateTime """All values less than or equal the given value.""" resetExpires_lte: DateTime """All values greater than the given value.""" resetExpires_gt: DateTime """All values greater than or equal the given value.""" resetExpires_gte: DateTime deletedAt: DateTime """All values that are not equal to given value.""" deletedAt_not: DateTime """All values that are contained in given list.""" deletedAt_in: [DateTime!] """All values that are not contained in given list.""" deletedAt_not_in: [DateTime!] """All values less than the given value.""" deletedAt_lt: DateTime """All values less than or equal the given value.""" deletedAt_lte: DateTime """All values greater than the given value.""" deletedAt_gt: DateTime """All values greater than or equal the given value.""" deletedAt_gte: DateTime lastLogin: DateTime """All values that are not equal to given value.""" lastLogin_not: DateTime """All values that are contained in given list.""" lastLogin_in: [DateTime!] """All values that are not contained in given list.""" lastLogin_not_in: [DateTime!] """All values less than the given value.""" lastLogin_lt: DateTime """All values less than or equal the given value.""" lastLogin_lte: DateTime """All values greater than the given value.""" lastLogin_gt: DateTime """All values greater than or equal the given value.""" lastLogin_gte: DateTime joinedAt: DateTime """All values that are not equal to given value.""" joinedAt_not: DateTime """All values that are contained in given list.""" joinedAt_in: [DateTime!] """All values that are not contained in given list.""" joinedAt_not_in: [DateTime!] """All values less than the given value.""" joinedAt_lt: DateTime """All values less than or equal the given value.""" joinedAt_lte: DateTime """All values greater than the given value.""" joinedAt_gt: DateTime """All values greater than or equal the given value.""" joinedAt_gte: DateTime isSuper: Boolean """All values that are not equal to given value.""" isSuper_not: Boolean } input UserWhereUniqueInput { id: ID email: String } `; export const Prisma = makePrismaBindingClass>({ typeDefs }); /** * Types */ export type UserOrderByInput = | 'id_ASC' | 'id_DESC' | 'email_ASC' | 'email_DESC' | 'password_ASC' | 'password_DESC' | 'name_ASC' | 'name_DESC' | 'inviteToken_ASC' | 'inviteToken_DESC' | 'inviteAccepted_ASC' | 'inviteAccepted_DESC' | 'emailConfirmed_ASC' | 'emailConfirmed_DESC' | 'emailConfirmToken_ASC' | 'emailConfirmToken_DESC' | 'resetToken_ASC' | 'resetToken_DESC' | 'resetExpires_ASC' | 'resetExpires_DESC' | 'deletedAt_ASC' | 'deletedAt_DESC' | 'lastLogin_ASC' | 'lastLogin_DESC' | 'joinedAt_ASC' | 'joinedAt_DESC' | 'isSuper_ASC' | 'isSuper_DESC' | 'updatedAt_ASC' | 'updatedAt_DESC' | 'createdAt_ASC' | 'createdAt_DESC'; export type MutationType = 'CREATED' | 'UPDATED' | 'DELETED'; export interface UserWhereUniqueInput { id?: ID_Input; email?: String; } export interface UserCreateInput { email: String; password: String; name: String; inviteToken?: String; inviteAccepted?: Boolean; emailConfirmed?: Boolean; emailConfirmToken?: String; resetToken?: String; resetExpires?: DateTime; deletedAt?: DateTime; lastLogin?: DateTime; joinedAt: DateTime; isSuper?: Boolean; } export interface UserUpdateInput { email?: String; password?: String; name?: String; inviteToken?: String; inviteAccepted?: Boolean; emailConfirmed?: Boolean; emailConfirmToken?: String; resetToken?: String; resetExpires?: DateTime; deletedAt?: DateTime; lastLogin?: DateTime; joinedAt?: DateTime; isSuper?: Boolean; } export interface UserSubscriptionWhereInput { AND?: UserSubscriptionWhereInput[] | UserSubscriptionWhereInput; OR?: UserSubscriptionWhereInput[] | UserSubscriptionWhereInput; NOT?: UserSubscriptionWhereInput[] | UserSubscriptionWhereInput; mutation_in?: MutationType[] | MutationType; updatedFields_contains?: String; updatedFields_contains_every?: String[] | String; updatedFields_contains_some?: String[] | String; node?: UserWhereInput; } export interface UserWhereInput { AND?: UserWhereInput[] | UserWhereInput; OR?: UserWhereInput[] | UserWhereInput; NOT?: UserWhereInput[] | UserWhereInput; id?: ID_Input; id_not?: ID_Input; id_in?: ID_Input[] | ID_Input; id_not_in?: ID_Input[] | ID_Input; id_lt?: ID_Input; id_lte?: ID_Input; id_gt?: ID_Input; id_gte?: ID_Input; id_contains?: ID_Input; id_not_contains?: ID_Input; id_starts_with?: ID_Input; id_not_starts_with?: ID_Input; id_ends_with?: ID_Input; id_not_ends_with?: ID_Input; email?: String; email_not?: String; email_in?: String[] | String; email_not_in?: String[] | String; email_lt?: String; email_lte?: String; email_gt?: String; email_gte?: String; email_contains?: String; email_not_contains?: String; email_starts_with?: String; email_not_starts_with?: String; email_ends_with?: String; email_not_ends_with?: String; password?: String; password_not?: String; password_in?: String[] | String; password_not_in?: String[] | String; password_lt?: String; password_lte?: String; password_gt?: String; password_gte?: String; password_contains?: String; password_not_contains?: String; password_starts_with?: String; password_not_starts_with?: String; password_ends_with?: String; password_not_ends_with?: String; name?: String; name_not?: String; name_in?: String[] | String; name_not_in?: String[] | String; name_lt?: String; name_lte?: String; name_gt?: String; name_gte?: String; name_contains?: String; name_not_contains?: String; name_starts_with?: String; name_not_starts_with?: String; name_ends_with?: String; name_not_ends_with?: String; inviteToken?: String; inviteToken_not?: String; inviteToken_in?: String[] | String; inviteToken_not_in?: String[] | String; inviteToken_lt?: String; inviteToken_lte?: String; inviteToken_gt?: String; inviteToken_gte?: String; inviteToken_contains?: String; inviteToken_not_contains?: String; inviteToken_starts_with?: String; inviteToken_not_starts_with?: String; inviteToken_ends_with?: String; inviteToken_not_ends_with?: String; inviteAccepted?: Boolean; inviteAccepted_not?: Boolean; emailConfirmed?: Boolean; emailConfirmed_not?: Boolean; emailConfirmToken?: String; emailConfirmToken_not?: String; emailConfirmToken_in?: String[] | String; emailConfirmToken_not_in?: String[] | String; emailConfirmToken_lt?: String; emailConfirmToken_lte?: String; emailConfirmToken_gt?: String; emailConfirmToken_gte?: String; emailConfirmToken_contains?: String; emailConfirmToken_not_contains?: String; emailConfirmToken_starts_with?: String; emailConfirmToken_not_starts_with?: String; emailConfirmToken_ends_with?: String; emailConfirmToken_not_ends_with?: String; resetToken?: String; resetToken_not?: String; resetToken_in?: String[] | String; resetToken_not_in?: String[] | String; resetToken_lt?: String; resetToken_lte?: String; resetToken_gt?: String; resetToken_gte?: String; resetToken_contains?: String; resetToken_not_contains?: String; resetToken_starts_with?: String; resetToken_not_starts_with?: String; resetToken_ends_with?: String; resetToken_not_ends_with?: String; resetExpires?: DateTime; resetExpires_not?: DateTime; resetExpires_in?: DateTime[] | DateTime; resetExpires_not_in?: DateTime[] | DateTime; resetExpires_lt?: DateTime; resetExpires_lte?: DateTime; resetExpires_gt?: DateTime; resetExpires_gte?: DateTime; deletedAt?: DateTime; deletedAt_not?: DateTime; deletedAt_in?: DateTime[] | DateTime; deletedAt_not_in?: DateTime[] | DateTime; deletedAt_lt?: DateTime; deletedAt_lte?: DateTime; deletedAt_gt?: DateTime; deletedAt_gte?: DateTime; lastLogin?: DateTime; lastLogin_not?: DateTime; lastLogin_in?: DateTime[] | DateTime; lastLogin_not_in?: DateTime[] | DateTime; lastLogin_lt?: DateTime; lastLogin_lte?: DateTime; lastLogin_gt?: DateTime; lastLogin_gte?: DateTime; joinedAt?: DateTime; joinedAt_not?: DateTime; joinedAt_in?: DateTime[] | DateTime; joinedAt_not_in?: DateTime[] | DateTime; joinedAt_lt?: DateTime; joinedAt_lte?: DateTime; joinedAt_gt?: DateTime; joinedAt_gte?: DateTime; isSuper?: Boolean; isSuper_not?: Boolean; } /* * An object with an ID */ export interface Node { id: ID_Output; } /* * Information about pagination in a connection. */ export interface PageInfo { hasNextPage: Boolean; hasPreviousPage: Boolean; startCursor?: String; endCursor?: String; } export interface UserPreviousValues { id: ID_Output; email: String; password: String; name: String; inviteToken?: String; inviteAccepted: Boolean; emailConfirmed: Boolean; emailConfirmToken?: String; resetToken?: String; resetExpires?: DateTime; deletedAt?: DateTime; lastLogin?: DateTime; joinedAt: DateTime; isSuper: Boolean; } export interface User extends Node { id: ID_Output; email: String; password: String; name: String; inviteToken?: String; inviteAccepted: Boolean; emailConfirmed: Boolean; emailConfirmToken?: String; resetToken?: String; resetExpires?: DateTime; deletedAt?: DateTime; lastLogin?: DateTime; joinedAt: DateTime; isSuper: Boolean; } /* * An edge in a connection. */ export interface UserEdge { node: User; cursor: String; } /* * A connection to a list of items. */ export interface UserConnection { pageInfo: PageInfo; edges: UserEdge[]; aggregate: AggregateUser; } export interface UserSubscriptionPayload { mutation: MutationType; node?: User; updatedFields?: String[]; previousValues?: UserPreviousValues; } export interface AggregateUser { count: Int; } export interface BatchPayload { count: Long; } export type DateTime = Date | string; /* The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `"4"`) or integer (such as `4`) input value will be accepted as an ID. */ export type ID_Input = string | number; export type ID_Output = string; /* The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1. */ export type Int = number; /* The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text. */ export type String = string; /* The `Boolean` scalar type represents `true` or `false`. */ export type Boolean = boolean; /* The `Long` scalar type represents non-fractional signed whole numeric values. Long can represent values between -(2^63) and 2^63 - 1. */ export type Long = string; ================================================ FILE: packages/graphql-authentication-prisma/src/index.ts ================================================ export { forwardTo } from './utils'; export { GraphqlAuthenticationPrismaAdapter } from './Prisma'; ================================================ FILE: packages/graphql-authentication-prisma/src/utils.ts ================================================ import { forwardTo as pForwardTo } from 'prisma-binding'; import { getUserId, Context } from 'graphql-authentication'; /** * @deprecated Use prisma-binding's forwardTo('db') method instead in combination with graphql-shield to handle permissions. */ export function forwardTo({ unauthorized, bindingName }: { unauthorized?: boolean; bindingName?: string; }) { return (parent: any, args: any, ctx: Context, info: any) => { if (!unauthorized) { getUserId(ctx); } return pForwardTo(bindingName || 'db')(parent, args, ctx, info); }; } ================================================ FILE: packages/graphql-authentication-prisma/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "compilerOptions": { "rootDir": "src", "outDir": "dist" }, "include": ["src/index.ts"] } ================================================ FILE: packages/graphql-authentication-prisma/tsconfig.json ================================================ { "extends": "../../tsconfig.json" } ================================================ FILE: tsconfig.base.json ================================================ { "compilerOptions": { "target": "ES2017", "module": "commonjs", "declaration": true, "lib": ["esnext"], "strict": true, // Disabled because this doesn't work correctly with "declaration": true "noUnusedLocals": false, "strictFunctionTypes": false, "noImplicitAny": false, "forceConsistentCasingInFileNames": true } } ================================================ FILE: tsconfig.json ================================================ { "extends": "./tsconfig.base.json", "compilerOptions": { "baseUrl": "./packages", "paths": { "graphql-authentication": ["./graphql-authentication/src"] } } } ================================================ FILE: tslint.json ================================================ { "extends": "@volst/tslint-config" }