Code Monkey home page Code Monkey logo

graphql-authentication's Introduction

GraphQL Authentication plugin for Craft CMS 4.0+

GraphQL Authentication adds a JWT authentication layer to your Craft CMS GraphQL endpoint.

Plugin Overview

  • Adds support for user registration and authentication (see Authentication)
  • Adds support for Two-Factor Authentication (see Two-Factor Authentication)
  • Adds support for Magic Link Authentication (see Magic Authentication)
  • Adds support for social sign-in – currently Google, Facebook, Twitter, Apple, and Microsoft (see Social)
  • Adds ability to define per-section user restrictions (queries and mutations can be limited to author-only) (see User Settings)
  • Checks mutation fields against schema permissions, and prevents fields being saved if user is trying to access private entries/assets
  • Adds ability to assign unique schemas for each user group
  • Adds ability to restrict user queries and mutations to Craft multi-site sites
  • Adds ability to mark fields as private – stopping users from querying/mutating fields on entries
  • Adds a unique, per-user query cache

Use the table below to determine which version of the plugin you should install.

Craft CMS Version Plugin Version
5 3
4 2
3 1

Documentation

You can view the documention for the plugin here.

graphql-authentication's People

Contributors

awasser-omnispear avatar boudewijn-zicht avatar brandonkelly avatar hendrik-agprop avatar jamesedmonston avatar jamesnuttall avatar nstcactus avatar robinbeatty avatar saychi avatar stalex89 avatar tam avatar zsavajji avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

graphql-authentication's Issues

Queries including `authorId` return incorrect results

I've got a section called courses and a schema called instructors. When I run a query like this:

query coursesQuery {
  entries(section: "courses", authorId: 610, status: null) {
    title
  }
}

I should only get back 2 courses.

It works correctly with the public schema, in GraphiQL explorer (with the instructors schema), and from https://graphiql-online.com/graphiql using a token I created for the instructors schema.

When I query with a JWT token configured for the same schema I get all courses, not limited by author.

I saw that the JWT token has an access token embedded within it. I tried using that token directly and got the same result (courses not limited by author). But when I disable the plugin, that token does return the correct results.

`updateViewer` mutation throwing

Adding this issue as a notice: the updateViewer mutation is currently throwing an error. I've fixed the issue in 8048d64 but there are a couple of other commits behind it that I need to test more before releasing an update.

Plugin prevents all GraphQL calls in Craft CMS 3.6.0.1

Hello,

I recently upgraded from Craft CMS 3.5.18 to 3.6.0.1. Subsequently, all my graphql calls return the following message:

{ "errors": [ { "message": "Call to a member function getFields() on string" } ] }

If I uninstall the auth plugin, the graphqls work fine.

I also had to bump my php version in composer from 7.2 to 7.3 for the new Craft update, if that may be the cause.

mutation Authenticate - strange unix date returned.

I'm testing the authenticate mutation:
There is a problem with the result:
"data": {
"authenticate": {
"jwtExpiresAt": 1616412117000,
"refreshTokenExpiresAt": 1624355517000,
}

000 is added to the end, making the unix data incorrect

But if you decode the JWT it's ok!

This is my environment:

Craft CMS 3.6.10
GraphQL Authentication: 1.7.0

PHP version: 7.3.20

Requests failing with public schema disabled

Hi James,
After the last update I noticed that the htaccess configuration present in my htaccess file:

RewriteCond% {HTTP: Authorization} ^ (. *)
RewriteRule. * - [e = HTTP_AUTHORIZATION:% 1]

seems no longer necessary ...
Indeed, this creates a Header Authorization problem.

Can you confirm me if the latest changes impact this configuration?

Craft Pro 3.6.12.1
GraphQL Authentication | 1.8.0

Support for custom elements

Hi James,

Continuing to evaluate the plugin, I was wondering if an how the plugin supports custom elements in managing access. Browsing through you code I see support for entries and assets. I have my own plugin where I use custom elements that I want to manage (read/write only by author) through the plugin.... Haven't tested it fully yes, hence the question if and how this could work

Question - Multiple JWT refresh tokens at login

Hi James,

We are doing some tests using your plugin to authenticate users through a mobile app accessing a Craft custom plugin's data. We got the authentication working through Apollo Graphql. One thing we noticed is that every time we authenticate a user in a test call, a new JWT refresh token is created. Is this intended behaviour ? Assuming for every new device a key record is created, but now the effect is that multiple JWT tokens exists supporting the same session. Do we need to keep track of authentication status on the mobile app to deal with this?

Activate User

After a user registers, and then goes to the activate url. Passing code/id to the set password mutation does not activate the user.

mutation SetPassword($password: String!, $code: String!, $id: String!) {
	setPassword(password: $password, code: $code, id: $id)
}

response:

{"setPassword": "Successfully saved password"}

Cheers

[Bug] "Undefined offset" when doing mutations

Thanks for this wonderful plugin. I'm hitting an error though when doing mutations.

The bug is located in TokenService.php inside _extractUserId(GqlToken $token).

I get Undefined offset: 1 PHP error when running return explode('-', $token->name)[1];

XDebugger shows that $token->name equals "userToken" with neither a dash or userId. So exploding $token->name returns only a single value array. That's why it throws with Undefined offset: 1 and aborts the request.

What would be a good approach to bugfix this issue?

Thanks!

Verify email via GraphQL Authentication

Hi @jamesedmonston

Is it possible to have a mutation that handles verifying emails? They're the URLs in the format of /verifyemail?code=XYZ which Craft sends out to new users.

I suspect it would be quite similar to how the setPassword mutation accepts the code

Ideally, we'd want our headless frontend handling those so that the backend URL isn't exposed.

Cheers,

Andrew

"Invalid Authorization Header" on Logout

Hi,
when I try to logout either with deleteCurrentToken or deleteAllTokens I get this error:

{
  "errors": [
    {
      "debugMessage": "Invalid Authorization Header",
      "message": "Internal server error",
      "category": "internal",
      "locations": [
        {
          "line": 3,
          "column": 3
        }
      ],
      "path": [
        "deleteCurrentToken"
      ]
    }
  ]
}

Does anyone had the same issue?
Thanks in advance and regards,

Davide

Reset password process/flow question

Hello! Thanks for this plugin, it's awesome.

I have a question about the 'Forgotten Password' / 'Set Password' flow, to allow a user to reset their password. This may be more of a Craft related question.

The mutation to send a password reset email makes sense to me. What I'm trying to then work out is how to route back to my application URL, with both the code and id query parameters required to then send/use the 'Set password' mutation.

Customising the Reset your password email template in Craft > Utils > System Messages looks like this -

Hey {{user.friendlyName}},

To reset your {{systemName}} password, click on this link:

<{{link}}>

If you were not expecting this email, just ignore it.

... with {{link}} generating a URL that looks like https://craft.test/admin/set-password?code=H6debAyhJ5JdKXbd8WleNhFNNUA6KhE3&id=49acc4ae-53d4-4e50-b553-15ee239ecba4

I'm trying to override {{link}} here and instead route to https://myapplication.test/page?code={{code}}&id={{id}}. Do you have any suggestions on how best to achieve this? Would using a twig |replace filter be the best option here?

Thanks!

Support for Google Sign-In

I saw this mentioned in #3 and wanted to call it out as a potential feature since it's something I could use in a current project. Hopefully this can get on the roadmap and maybe I can even sponsor the feature as @timkelty is helping us with the JWT integration as well.

A usage question

Really glad to have spotted this plug in. I'm wondering if you think it will help in my use case.

I've two seperate domains/craft installs but the client is looking for the user auth to be shared between them both.

I'd considered using Laravel on one of the sites to "talk" to craft, maybe via graphql, to get this working.

Any thoughts on if I'm barking up the wrong tree here?

Passing an malformed JWT token throws an error

When passing an malformed JWT token, this error is thrown:

Argument 1 passed to GraphQL\\Type\\Definition\\Type::getNullableType() must be an instance of GraphQL\\Type\\Definition\\Type, null given, called in /var/www/html/vendor/webonyx/graphql-php/src/Validator/Rules/ValuesOfCorrectType.php on line 75

Calling unknown method: craft\console\Request::getBodyParam()

Once the plugin has been installed, subsequent project config syncs are throwing Calling unknown method: craft\console\Request::getBodyParam().

Need to check for Craft::$app->getRequest()->isConsoleRequest and not run the mutation restrictions.

Support different schemas per user group

It would be nice to have the ability to assign schemas per user group. When authenticating, tokens would be assigned to the respective schema.

  • Add settings to choose which schema user groups get assigned to
  • Add settings to enable/disable per-user group registration mutations
  • Assign tokens to schemas based on user group
  • Add separate register mutations per user group

Invalid Authorization Header

Hi @ all

I just quick install this plugin and tried to use. Now i become on every request an error with a anonymus/guest/no barear token user.

Response:

{
  "errors": [
    {
      "debugMessage": "Invalid Authorization Header",
      "message": "Internal server error",
      "category": "internal",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "entries"
      ]
    }
  ],
  "data": {
    "entries": null
  }
}

After i disabed this plugin. It works great again. BTW: the graphQL dashboard doesn't work too.
Has anybody a idea?

Support JWT / Refresh tokens

Side note: This looks great! I was just about to start a project where I would have had to do nearly the same thing. Thanks!

Did you/have you considered using JWT at all?

As I understand it, the downside of doing things how you are is the client is forced to store the access token, likely insecurely (eg localstorage). So you're left with either long expiry (less secure, hard to invalidate) or short ones (user keeps getting logged out).

With JWT, this is usually mitigated by making the JWT expiry super short, and refreshing it with a "refresh token", as outlined here: https://hasura.io/blog/best-practices-of-using-jwt-with-graphql/#silent_refresh

JWT also gives you the opportunity to get back some data about the user (e.g. member group), should you want to do something with permissions, but non-gql based (e.g. routing).

Ensure Auth0 tokens work

Based on some feedback in Discord, it seems that tokens created via Auth0 (and linked to a Google Client ID) may not be working correctly.

Need to test to get to the bottom of how/why this is.

Improve documentation

Documentation needs fleshing out with richer examples and walkthroughs (maybe a screen recording running through query/mutation examples).

Unable to set category field on register mutation

I'm not sure where I'm going wrong here, but I seem to be unable to set a category for a given user using the register mutation.

Given this mutation:

mutation MyMutation {
  register(email: "[email protected]", firstName: "qqq", lastName: "asdfsdf", password: "wesafdasdfsadf", legacyId: 6634, organisation: 6634) {
    jwt
    jwtExpiresAt
    user {
      id
      firstName
      lastName
      legacyId
      organisation {
        id
        title
      }
    }
  }
}

The legacyId custom field is set to 6634 but the organisation, whose id is 6634 definitely exists, is not set. I've tried [6634] and "6634" as values, but none of them work. Any ideas where I'm going wrong?

Use encoded JWT payload to authorise calls

Hi @ all

Why you generate a graphQL token? After i have logged in. There are some graphQL token available.

Screenshot 2020-12-09 at 12 59 32

The schema is already saved in the jwt token:
Screenshot 2020-12-09 at 13 02 13

It's just a overhead, that you must every time to check and sync with the database. How do you check the jwt token, via signature or database?

Error on install

When i try to install the plugin directly from the store, i have this error :

ParseError: syntax error, unexpected ')'

Craft version : Craft Pro 3.5.15.1

Capture d’écran 2020-11-13 à 09 20 34

Type Error when accessing settings

Me again. I'm just going to keep breaking your plugin all weekend. 😀

Trying to access the settings page and I'm getting this:

TypeError: Argument 1 passed to jamesedmonston\graphqlauthentication\controllers\SettingsController::_getSchemaPermissions() must be an instance of craft\models\GqlSchema, null given, called in /home/hcps/webapps/hcps-api-dev/releases/19869138bf56138d7e6b78bb944872f5b520d23f/craft/vendor/jamesedmonston/graphql-authentication/src/controllers/SettingsController.php on line 120 and defined in /home/hcps/webapps/hcps-api-dev/releases/19869138bf56138d7e6b78bb944872f5b520d23f/craft/vendor/jamesedmonston/graphql-authentication/src/controllers/SettingsController.php:148
Stack trace:
#0 /home/hcps/webapps/hcps-api-dev/releases/19869138bf56138d7e6b78bb944872f5b520d23f/craft/vendor/jamesedmonston/graphql-authentication/src/controllers/SettingsController.php(120): jamesedmonston\graphqlauthentication\controllers\SettingsController->_getSchemaPermissions()
#1 [internal function]: jamesedmonston\graphqlauthentication\controllers\SettingsController->actionIndex()
#2 /home/hcps/webapps/hcps-api-dev/releases/19869138bf56138d7e6b78bb944872f5b520d23f/craft/vendor/yiisoft/yii2/base/InlineAction.php(57): call_user_func_array()
#3 /home/hcps/webapps/hcps-api-dev/releases/19869138bf56138d7e6b78bb944872f5b520d23f/craft/vendor/yiisoft/yii2/base/Controller.php(180): yii\base\InlineAction->runWithParams()
#4 /home/hcps/webapps/hcps-api-dev/releases/19869138bf56138d7e6b78bb944872f5b520d23f/craft/vendor/craftcms/cms/src/web/Controller.php(190): yii\base\Controller->runAction()
#5 /home/hcps/webapps/hcps-api-dev/releases/19869138bf56138d7e6b78bb944872f5b520d23f/craft/vendor/yiisoft/yii2/base/Module.php(528): craft\web\Controller->runAction()
#6 /home/hcps/webapps/hcps-api-dev/releases/19869138bf56138d7e6b78bb944872f5b520d23f/craft/vendor/craftcms/cms/src/web/Application.php(274): yii\base\Module->runAction()
#7 /home/hcps/webapps/hcps-api-dev/releases/19869138bf56138d7e6b78bb944872f5b520d23f/craft/vendor/yiisoft/yii2/web/Application.php(103): craft\web\Application->runAction()
#8 /home/hcps/webapps/hcps-api-dev/releases/19869138bf56138d7e6b78bb944872f5b520d23f/craft/vendor/craftcms/cms/src/web/Application.php(259): yii\web\Application->handleRequest()
#9 /home/hcps/webapps/hcps-api-dev/releases/19869138bf56138d7e6b78bb944872f5b520d23f/craft/vendor/yiisoft/yii2/base/Application.php(386): craft\web\Application->handleRequest()
#10 /home/hcps/webapps/hcps-api-dev/releases/19869138bf56138d7e6b78bb944872f5b520d23f/public/index.php(21): yii\base\Application->run()
#11 {main}

Any thoughts? I did just delete a schema, which may have been applied to a group. I probably should have removed it from the group first, yeah?

Header Authorization problem - after upgrade

Hi James,
After a test on production enviroment, if I remove the htaccess directive, the login run OK BUT all others GraphQL has problem:

{
    "errors": [
        {
            "message": "Tried to load an unregistered type \"xxxxx_GlobalSet\". This can indicate both a typo in the query or an issue with the schema used."
        }
    ]
}

If I enable the htaccess directive ( after login ) all are ok.

There is a Authorization header problem into the plugin....

Originally posted by @GMConsultant in #52 (comment)

Entry queries without section argument are scoped to author, regardless of settings

I'm having an issue when performing queries without a section argument (I quite often already have an ID or URI from another query). For example, if I query the following with a valid JWT - no entry is returned. But without the JWT, the query succeeds as it should.
query { entry(uri: "about") { id } }
For reference, I'm not currently using the "Restricted Entry Queries" sections for my schema, but that doesn't appear to make any change to the logic.

Looking at the Entry resolver file:

$arguments['authorId'] = $user->id;

It looks as though the else statement above is choosing to scope the query to the owner of the JWT when the section argument isn't set.

Am I doing something wrong here, is this a limitation for some other logic or a bug?

Thanks!

No results on JWT authenticated request

This may be related to #23 but just in case it's a separate issue I'll post it here. I'm getting no results on my authenticated requests, even though the underlying schema should have the correct access. Here's an overview of what's happening, let me know if this makes sense or if I've missed anything:

  • I have a GraphQL schema called "Website" which gives access to all of the public sections of the website.
  • I've created a token for the Website schema.
  • I'm able to call my query with the token and get the expected response (Screenshot 1)

Next, I've enabled the GraphQL Authentication plugin and set up the "Multiple Schemas" permission type.

  • I've got a group called "Users" with no CP access, which are mapped to the "Website" schema. (Screenshot 2)
  • I use the GoogleSignInUsers mutation with my idToken to get a JWT token (Screenshot 3).
  • I use the JWT token with the same query, which appears to run successful but returns an empty result (Screenshot 4).

Any thoughts on what could be happening here?
screenshot 1
screenshot 2
screenshot 3
screenshot 4

Adding new GraphQL schema doesn't update granularSchemas setting

(I'm running on the develop branch @ 7109e21)

I've got permissionType set to multiple. I created a new user group and new GraphQL schema to apply to the group. When trying to access the /admin/graphql-authentication/settings page I get a PHP exception:

yii\base\ErrorException: Undefined index: group-4 in /home/ubuntu/sites/hcps-api/craft/vendor/jamesedmonston/graphql-authentication/src/controllers/SettingsController.php:109
Stack trace:
#0 /home/ubuntu/sites/hcps-api/craft/vendor/craftcms/cms/src/web/ErrorHandler.php(76): yii\base\ErrorHandler->handleError()
#1 /home/ubuntu/sites/hcps-api/craft/vendor/jamesedmonston/graphql-authentication/src/controllers/SettingsController.php(109): craft\web\ErrorHandler->handleError()
#2 [internal function]: jamesedmonston\graphqlauthentication\controllers\SettingsController->actionIndex()
#3 /home/ubuntu/sites/hcps-api/craft/vendor/yiisoft/yii2/base/InlineAction.php(57): call_user_func_array()
#4 /home/ubuntu/sites/hcps-api/craft/vendor/yiisoft/yii2/base/Controller.php(180): yii\base\InlineAction->runWithParams()
#5 /home/ubuntu/sites/hcps-api/craft/vendor/craftcms/cms/src/web/Controller.php(190): yii\base\Controller->runAction()
#6 /home/ubuntu/sites/hcps-api/craft/vendor/yiisoft/yii2/base/Module.php(528): craft\web\Controller->runAction()
#7 /home/ubuntu/sites/hcps-api/craft/vendor/craftcms/cms/src/web/Application.php(274): yii\base\Module->runAction()
#8 /home/ubuntu/sites/hcps-api/craft/vendor/yiisoft/yii2/web/Application.php(103): craft\web\Application->runAction()
#9 /home/ubuntu/sites/hcps-api/craft/vendor/craftcms/cms/src/web/Application.php(259): yii\web\Application->handleRequest()
#10 /home/ubuntu/sites/hcps-api/craft/vendor/yiisoft/yii2/base/Application.php(386): craft\web\Application->handleRequest()
#11 /home/ubuntu/sites/hcps-api/public/index.php(21): yii\base\Application->run()
#12 {main}

It seems the granularSchemas setting is not updated with a key for the new group. Adding the group to project.yaml and applying the changes manually seems to work.

SQL error 42703 on Postgres when registering a new new user

Hello,

I am evaluating your plugin. When trying out a couple of first command I ran into this issue when registering a new user:

query was:

mutation Register {
  register(
    email: "[email protected]"
    password: "koekoek"
    firstName: "Joske"
    lastName: "Vermeulen"
  ) {
    jwt
    jwtExpiresAt
    refreshToken
    refreshTokenExpiresAt
    user {
      id
      fullName
    }
  }
}

Response error:

"errors": [
    {
      "debugMessage": "SQLSTATE[42703]: Undefined column: 7 ERROR:  column \"expirydate\" does not exist\nLINE 3: WHERE (expiryDate <= CURRENT_TIMESTAMP) AND (name LIKE \"%use...\n               ^\nHINT:  Perhaps you meant to reference the column \"gqltokens.expiryDate\".",
      "message": "Internal server error",
      "extensions": {
        "category": "internal"
      },
...

I am running Craft version 3.6.10 on a Postgres 11.10 database. I suspect this error is due to the Postgres update statement.
Seems to be an issue with the expirydate passed on completely lowercase in stead of camelcase.

I managed to get the Register to work when I adapted the code that failed like below:

Tokenservice line 375:

$gqlTokens = RecordsGqlToken::find()->where('"expiryDate" <= CURRENT_TIMESTAMP')->andWhere(['like', 'name', '%user-%'])->all();

Tokenservoce line 383

$refreshTokens = RefreshToken::find()->where('"expiryDate" <= CURRENT_TIMESTAMP')->all();

Google Authentication error: Undefined index: family_name

I believe my Google profile does not have the family_name value set, I'm getting this error when trying to use Google auth:

{
  "errors": [
    {
      "debugMessage": "Undefined index: family_name",
      "message": "Internal server error",
      "category": "internal",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "googleSignInUsers"
      ]
    }
  ]
}

Enhanced activation functionality

Would it be possible to show a different error message if we can't authenticate because the user hasn't yet activated their screen? Also, would it be possible to have a mutation that triggers the activation email being sent?

With these two, we would be able to show a message like

You're not yet activated, check your email and click the link to verify your account or <a>click here to resent that email</a>

Last login not updating on users

Currently the register and authenticate mutations aren't updating the 'last login' field on users, so they'll be blank unless they log in to the control panel directly.

Possible issue: Double-packed array found

When running ./craft utils/repair/project-config I get the following output:

Repairing project config ...
- double-packed array found at plugins.graphql-authentication.settings.entryMutations
- double-packed array found at plugins.graphql-authentication.settings.entryQueries
- double-packed array found at plugins.graphql-authentication.settings.granularSchemas
- double-packed array found at plugins.graphql-authentication.settings.granularSchemas.group-1
Finished repairing project config

Not sure but might be related to craftcms/cms#5525 (comment) and how data is stored in the plugin settings model as described on https://craftcms.com/docs/3.x/extend/plugin-settings.html#settings-model: any complex or nested data types that aren’t compatible with toArray() should be excluded from fields() and declared in extraFields() instead.

Are those project config warnings above reason enough to consider this a bug?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.