Slide 1

Slide 1 text

AUTH IN THE GraphQL ERA

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

Don't talk with me, I'm a picture!

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

evilmartians.com

Slide 7

Slide 7 text

TATARSTAN

Slide 8

Slide 8 text

Authorization 90 % GraphQL 10 % Buzzwords 
 of 
 GraphQL 100 % How do I see my talk What do people expect

Slide 9

Slide 9 text

https://book.productionreadygraphql.com/

Slide 10

Slide 10 text

GraphQL Auth through the prism of RESTful Auth

Slide 11

Slide 11 text

Standard Rails App DB UsersController PostsController GET /users POST /users ImagesController ... GET /posts/:id PUT /posts/:id ... GET /images POST /images ... ... BUSINESS LOGIC LAYER

Slide 12

Slide 12 text

Standard Rails App DB UsersController PostsController GET /users POST /users ImagesController ... GET /posts/:id PUT /posts/:id ... GET /images POST /images ... ... BUSINESS LOGIC LAYER User

Slide 13

Slide 13 text

Standard Rails App DB UsersController PostsController GET /users POST /users ImagesController ... GET /posts/:id PUT /posts/:id ... GET /images POST /images ... ... BUSINESS LOGIC LAYER Data

Slide 14

Slide 14 text

Standard Rails App DB UsersController PostsController GET /users POST /users ImagesController ... GET /posts/:id PUT /posts/:id ... GET /images POST /images ... ... BUSINESS LOGIC LAYER RESTFul

Slide 15

Slide 15 text

Rails DHH way it's me

Slide 16

Slide 16 text

Standard* Rails App DB UsersController PostsController GET /users POST /users ImagesController ... GET /posts/:id PUT /posts/:id ... GET /images POST /images ... ... BUSINESS LOGIC LAYER *ACCESS CONTROL
 LAYER RESTFul

Slide 17

Slide 17 text

REST\RESTfull is a software architectural style that defines a set of constraints to be used for creating web services (c) Wikipedia

Slide 18

Slide 18 text

GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data.

Slide 19

Slide 19 text

DB GraphqlController POST /graphql BUSINESS LOGIC LAYER GraphQL Rails App GRAPHQL INTERNALS

Slide 20

Slide 20 text

DB GraphqlController POST /graphql BUSINESS LOGIC LAYER GraphQL Rails App GRAPHQL INTERNALS User Data

Slide 21

Slide 21 text

DB GraphqlController POST /graphql BUSINESS LOGIC LAYER GraphQL Rails App GRAPHQL INTERNALS ???

Slide 22

Slide 22 text

GraphqlControll#execute class GraphqlControll < ApplicationController!::Base def execute query = params[:query] result = AppSchema.execute(query, params: params) render json: result end end

Slide 23

Slide 23 text

GraphQL Internals

Slide 24

Slide 24 text

AppSchema#execute(q, p) GraphQL Internals GraphqlController POST /graphql

Slide 25

Slide 25 text

AppSchema#execute(q, p) Types-- Mutations-- GraphQL Internals GraphqlController POST /graphql *R** C*UD

Slide 26

Slide 26 text

AppSchema#execute(q, p) GraphQL Internals Types-- Mutations-- POST /graphql TagType UserType PostType ImageType ProfileType CommentType CreatePostMutation AddCommentMutation UpdateProfileMutation GraphqlController

Slide 27

Slide 27 text

AppSchema#execute(q, p) GraphQL Internals Types-- Mutations-- POST /graphql TagType UserType PostType ImageType ProfileType CommentType CreatePostMutation AddCommentMutation UpdateProfileMutation GraphqlController

Slide 28

Slide 28 text

AppSchema#execute(q, p) GraphQL Internals Types-- Mutations-- POST /graphql TagType UserType PostType ImageType ProfileType CommentType CreatePostMutation AddCommentMutation UpdateProfileMutation GraphqlController

Slide 29

Slide 29 text

AppSchema#execute(q, p) GraphQL Internals Types-- Mutations-- POST /graphql TagType UserType PostType ImageType ProfileType CommentType CreatePostMutation AddCommentMutation UpdateProfileMutation GraphqlController

Slide 30

Slide 30 text

When You Read First Pages of GraphQL Documentation

Slide 31

Slide 31 text

*ACCESS CONTROL
 LAYER DB GraphqlController POST /graphql BUSINESS LOGIC LAYER GraphQL* Rails App GRAPHQL ENGINE

Slide 32

Slide 32 text

Authentication before authorization

Slide 33

Slide 33 text

Authentication WHO IS WHO? Homer#0 Homer#1 Homer#2 Homer#3

Slide 34

Slide 34 text

Authentication Auth Framework User Model Crypto Layer Helpers (e.g. emails) ... (e.g. devise) GraphQL RESTfull ? How to use Framework ?

Slide 35

Slide 35 text

AppSchema#execute Login\Logout? Types-- Mutations-- POST /graphql TagType UserType PostType ImageType ProfileType CommentType LoginUser LogoutUser UpdateProfileMutation GraphqlController

Slide 36

Slide 36 text

AppSchema#execute Login\Logout? Types-- Mutations-- POST /graphql TagType UserType PostType ImageType ProfileType CommentType LoginUser LogoutUser UpdateProfileMutation GraphqlController # NOT NECESSARY

Slide 37

Slide 37 text

AppSchema#execute Login\Logout? Types-- Mutations-- POST /graphql TagType UserType PostType ImageType ProfileType CommentType CreatePostMutation AddCommentMutation UpdateProfileMutation GraphqlController GOOD [DeviseControllers]

Slide 38

Slide 38 text

class GraphqlControll < ApplicationController!::Base def execute query = params[:query] context = {current_user: get_user_from_api_token} result = AppSchema.execute(query, context: context) render json: result end end GraphqlControll#execute

Slide 39

Slide 39 text

UsersController PostsController POST /users ImagesController ... GET /posts/:id PUT /posts/:id ... GET /images POST /images ... *ACCESS CONTROL
 LAYER GET /users Ensuring Authentication

Slide 40

Slide 40 text

before_action :authenticate_user! UsersController PostsController POST /users ImagesController ... GET /posts/:id PUT /posts/:id ... GET /images POST /images ... *ACCESS CONTROL
 LAYER GET /users

Slide 41

Slide 41 text

AppSchema#execute The One Entrypoint Types-- Mutations-- POST /graphql TagType UserType PostType ImageType ProfileType CommentType CreatePostMutation AddCommentMutation UpdateProfileMutation GraphqlController

Slide 42

Slide 42 text

AppSchema#execute The One Entrypoint Types-- Mutations-- POST /graphql TagType UserType PostType ImageType ProfileType CommentType CreatePostMutation AddCommentMutation UpdateProfileMutation GraphqlController

Slide 43

Slide 43 text

Welcome graphql-ruby! ⭐ 4.1k on Github

Slide 44

Slide 44 text

class GraphqlControll < ApplicationController!::Base def execute query = params[:query] context = {current_user: get_user_from_api_token} result = AppSchema.execute(query, context: context) render json: result end end GraphqlControll#execute * context - state passed through the entire request

Slide 45

Slide 45 text

AppSchema#execute(q, p) GraphQL Internals Types-- Mutations-- POST /graphql TagType UserType PostType ImageType ProfileType CommentType CreatePostMutation AddCommentMutation UpdateProfileMutation GraphqlController

Slide 46

Slide 46 text

class UserType < BaseType field :profile, ProfileType def profile user.profile end end UserType !-> ProfileType class ProfileType < BaseType field :full_name !!... end

Slide 47

Slide 47 text

class UserType < BaseType field :profile, ProfileType def profile raise NotAuthorized unless context[:current_user]&.is_a?(User) user.profile end end UserType !-> ProfileType

Slide 48

Slide 48 text

class UserType < BaseType field :profile, ProfileType def profile raise NotAuthorized unless context[:current_user]&.is_a?(User) user.profile end end UserType !-> ProfileType Maintainability

Slide 49

Slide 49 text

We Need Some Magic!

Slide 50

Slide 50 text

class UserType < BaseType field :profile, ProfileType def profile user.profile end def visible?(context) raise NotAuthorized unless context[:current_user]&.is_a?(User) end end Visibility * visibility - special graphql-ruby feature

Slide 51

Slide 51 text

module Extensions module Visibility def visible?(context) return context[:current_user]&.is_a?(User) super end end end BaseType.prepend(Extensions!::Visibility) Visibility Extension

Slide 52

Slide 52 text

module Extensions module Visibility def visible?(context) return context[:current_user]&.is_a?(User) super end end end BaseType.prepend(Extensions!::Visibility) Visibility Extension Global

Slide 53

Slide 53 text

Visibility Extension module Extensions module Visibility def initialize(*args, login_required: false, !**kwargs, &block) super(*args, !**kwargs, &block) @login_required = login_required end def visible?(context) return context[:current_user]&.is_a?(User) if @login_required super end end end

Slide 54

Slide 54 text

class UserType < BaseType field :posts, PostType.connection_type with_options login_required: true do field :profile, ProfileType end end Visibility Extension #

Slide 55

Slide 55 text

CommentType Why Is Visibility Better Then Other Ways?

Slide 56

Slide 56 text

Why Is Visibility Better? CommentTyp # GraphQL query { !__schema { types { name } } } GraphQL Introspection

Slide 57

Slide 57 text

AppSchema#execute Types-- Mutations-- POST /graphql TagType UserType PostType ImageType ProfileType CommentType CreatePostMutation AddCommentMutation UpdateProfileMutation GraphqlController Hide Parts of Your Schema

Slide 58

Slide 58 text

Authorization

Slide 59

Slide 59 text

Authorization CAN this HOMER HAVE A BEER?

Slide 60

Slide 60 text

Authorization CAN this HOMER HAVE A BEER?

Slide 61

Slide 61 text

class UserType < BaseType field :posts, PostType.connection_type with_options login_required: true, admin: true do field :profile, ProfileType end end And Again Visibility?

Slide 62

Slide 62 text

Authorization is Flexible (current_user has role) !|| (current_user has permission) !|| (business rule) !|| !!...

Slide 63

Slide 63 text

class UserType < BaseType def self.authorized?(object, context) (current_user has role) !|| (current_user has permission) !|| (business rule) !|| end end GraphQL Authorization * authorized? - graphql-ruby feature

Slide 64

Slide 64 text

Authorization Auth Library API Abilities Layer Helpers (e.g. for templates) ... (e.g. cancan or pundit)

Slide 65

Slide 65 text

Authorization Auth Library API Abilities Layer Helpers (e.g. for templates) ... (e.g. cancan or pundit) GraphQL RESTfull ? How to use Library ?

Slide 66

Slide 66 text

Ability For Each Action UsersController PostsController POST /users ImagesController ... GET /posts/:id PUT /posts/:id ... GET /images POST /images ... *ACCESS CONTROL
 LAYER GET /users can? :read, Image can? :create, Image can? :read, @post can? :update, @post !!...

Slide 67

Slide 67 text

Ability For Each Action UsersController PostsController POST /users ImagesController ... GET /posts/:id PUT /posts/:id ... GET /images POST /images ... *ACCESS CONTROL
 LAYER GET /users can? :read, Image can? :create, Image can? :read, @post can? :update, @post !!... CONTROLLER RESOURCE

Slide 68

Slide 68 text

AppSchema#execute Types-- Mutations-- POST /graphql TagType UserType PostType ImageType ProfileType CommentType CreatePostMutation AddCommentMutation UpdateProfileMutation GraphqlController Ability For Each Node

Slide 69

Slide 69 text

AppSchema#execute Types-- Mutations-- POST /graphql TagType UserType PostType ImageType ProfileType CommentType CreatePostMutation AddCommentMutation UpdateProfileMutation GraphqlController Ability For Each Node author (String) body (String)

Slide 70

Slide 70 text

1+ Endpoints for Sets of Fields PostsController GET /posts/:id PUT /posts/:id ... *ACCESS CONTROL
 LAYER Admin::PostsController GET /admin/posts/:id PUT /admin/posts/:id ... Post id title body published_at

Slide 71

Slide 71 text

Can We (re)Use Existing Abilities Layer?

Slide 72

Slide 72 text

Yes, but ... • How to pass the current_user? • How to handle unauthorized error? • How to ensure authorization happens? • Hot to get rid of boilerplate? ... class UserType < BaseType field :profile, ProfileType def profile profile = user.profile can :read?, profile profile end end

Slide 73

Slide 73 text

class UserType < BaseType field :profile, ProfileType def profile profile = user.profile can :read?, profile profile end end Yes, but ... • How to pass the current_user? • How to handle unauthorized error? • How to ensure authorization happens? • Hot to get rid of boilerplate? ... Library Helpers

Slide 74

Slide 74 text

GraphQL Pro

Slide 75

Slide 75 text

Who Has Heard about gem "action_policy"?

Slide 76

Slide 76 text

gem "action_policy" The Authorization Framework

Slide 77

Slide 77 text

gem "action_policy" + GraphQL !== gem "action_policy-graphql"

Slide 78

Slide 78 text

class UserType < BaseType field :profile, ProfileType, authorize: true end class ProfilePolicy < ApplicationPolicy def show? current_user.id !== record.id end end As Easy as Possible

Slide 79

Slide 79 text

class UserType < BaseType field :profile, ProfileType, authorize: true end class ProfilePolicy < ApplicationPolicy def show? current_user.id !== record.id end end As Easy as Possible

Slide 80

Slide 80 text

# in your schema file rescue_from(ActionPolicy!::Unauthorized) do |exp| raise GraphQL!::ExecutionError.new( exp.result.message, extensions: { code: :unauthorized, fullMessages: exp.result.reasons.full_messages, details: exp.result.reasons.details } ) end Handling Exceptions

Slide 81

Slide 81 text

# All Docs GraphQL Features

Slide 82

Slide 82 text

AppSchema#execute Types-- Mutations-- POST /graphql TagType UserType PostType ImageType ProfileType CommentType CreatePostMutation AddCommentMutation UpdateProfileMutation GraphqlController Scoping Data admin? manager? owner? guest?

Slide 83

Slide 83 text

class UserType < BaseType field :posts, PostType.connection_type, authorized_scope: true end class PostPolicy < ApplicationPolicy # rel !== user.posts relation_scope do |rel| next rel.with_deleted if current_user.admin? next rel if user !== current_user rel.where(published: true) end end Scoping Data

Slide 84

Slide 84 text

UI/UX CAN HOMER PRESS THE BUTTON?

Slide 85

Slide 85 text

Where is the best place for UX element display logic? On Backend On Frontend On Both sides UI/UX 1. 2. 3.

Slide 86

Slide 86 text

On Backend On Frontend On Both sides UI/UX Expose Where is the best place for UX element display logic?

Slide 87

Slide 87 text

class PostType < BaseType expose_authorization_rules :edit?, :destroy? end UI/UX

Slide 88

Slide 88 text

UI/UX { post(id: $id) { canEdit { value # bool message # top-level decline message reasons { # detailed information details fullMessage } } !!... }

Slide 89

Slide 89 text

Hot Abilities

Slide 90

Slide 90 text

AppSchema#execute Types-- Mutations-- POST /graphql TagType UserType PostType ImageType ProfileType CommentType CreatePostMutation AddCommentMutation UpdateProfileMutation GraphqlController Hot Abilities can :edit?

Slide 91

Slide 91 text

class UserType < BaseType field :profile, ProfileType, authorize: true end class ProfilePolicy < ApplicationPolicy cache :show? def show? # heavy calculation end end Let's Cache it!

Slide 92

Slide 92 text

Why We Are Talking Only About Reading Data?

Slide 93

Slide 93 text

AppSchema#execute GraphQL Internals Types-- Mutations-- POST /graphql TagType UserType PostType ImageType ProfileType CommentType CreatePostMutation AddCommentMutation UpdateProfileMutation GraphqlController

Slide 94

Slide 94 text

class CreatePostMutation < BaseMutation !!... def resolve(post:, *attrs) authorize! post, to: :create? # business logic here end end Mutations !!<=> Service Objects

Slide 95

Slide 95 text

class CreatePostMutation < BaseMutation !!... def resolve(post:, *attrs) authorize! post, to: :create? # business logic here end end Mutations !!<=> Service Objects # after_action :verify_authorized How to ensure policies are used?

Slide 96

Slide 96 text

Ensuring Policies are Used class BaseMutation < GraphQL!::Schema!::RelayClassicMutation after_resolve do raise "Unauthorized mutation" unless @authorization_performed end def authorize!(*) @authorization_performed = true super end def skip_authorization! @authorization_performed = true end end *Full gist: https://bit.ly/2UYZpSX

Slide 97

Slide 97 text

Ensuring Policies are Used class BaseMutation < GraphQL!::Schema!::RelayClassicMutation after_resolve do raise "Unauthorized mutation" unless @authorization_performed end def authorize!(*) @authorization_performed = true super end def skip_authorization! @authorization_performed = true end end *Full gist: https://bit.ly/2UYZpSX

Slide 98

Slide 98 text

Testing

Slide 99

Slide 99 text

Only Integration Tests describe PostType do let(:query) do !<<~GRAPHQL query($id: ID!){ node(id: $id) { !!... } } GRAPHQL end let(:context) { current_user: create(:user) } it "checks execution" do result = AppSchema.execute(query, context: context, !!...) expect(result["data"]["node"][!!...]).to eq(!!...) end end

Slide 100

Slide 100 text

Only Integration Tests describe PostType do let(:query) do !<<~GRAPHQL query($id: ID!){ node(id: $id) { !!... } } GRAPHQL end let(:context) { current_user: create(:user) } it "checks execution" do result = AppSchema.execute(query, context: context, !!...) expect(result["data"]["node"][!!...]).to eq(!!...) end end

Slide 101

Slide 101 text

Auth Tests describe PostType do describe "#somefield" do context "when user is admin" do let(:current_user) { create(:admin) } # it !!... end context "when user is owner" do let(:current_user) { create(:user, post: post) } # it !!... end context "when user is guest" do let(:current_user) { nil } # it !!... end end end

Slide 102

Slide 102 text

Bad Auth Tests Testing is not what they should Slow Complex describe PostType do describe "#somefield" do context "when user is admin" do let(:current_user) { create(:admin) } # it !!... end context "when user is owner" do let(:current_user) { create(:user, post: post) } # it !!... end context "when user is guest" do let(:current_user) { nil } # it !!... end end end

Slide 103

Slide 103 text

Good Auth Tests describe PostType do let(:post) { create(:post) } subject { # call AppSchema.execute(id: post.id) here } describe "#somefield" do it "is authorized" do expect { subject }.to be_authorized_to(:show?, post) .with(PostPolicy) end end end

Slide 104

Slide 104 text

Conclusion

Slide 105

Slide 105 text

Conclusion Shared Auth* Libraries Features Features RESTful GraphQL

Slide 106

Slide 106 text

Conclusion Shared Auth* Libraries Features Features RESTful GraphQL / / / / / / /

Slide 107

Slide 107 text

Conclusion Shared Auth* Libraries Features Features RESTful GraphQL / / / / / / /

Slide 108

Slide 108 text

@ssnickolay Nikolay Sverchkov @ssnickolay THANK YOU! @evilmartians http://evl.ms/blog http://evl.ms/telegram