gqlgen icon indicating copy to clipboard operation
gqlgen copied to clipboard

How do i create `skipAuth` directives.

Open TheDhejavu opened this issue 1 year ago • 1 comments

What happened?

I am working on adding a skipAuth directives to queries that will render certain queries accessible to the public, provided the skipAuth directive is specified.

What did you expect?

Referring to this document (https://gqlgen.com/reference/directives/), the directive operates subsequent to the authorization middleware. My goal is to find a strategy that facilitates the execution of the directive or checks if skipAuth is specified within the auth middleware before moving forward.

Minimal graphql.schema and models to reproduce

type Query {
	gates: Gate! @skipAuth
}

versions

  • go run github.com/99designs/gqlgen version? v0.17.16
  • go version? go1.19.1 darwin/amd64

TheDhejavu avatar May 31 '23 08:05 TheDhejavu

for future travellers. Here is my hacky solution to this

func skipAuth(graphqlExecutableSchema graphql.ExecutableSchema, r *http.Request) (bool, error) {
	var skipAuth bool
	var req struct {
		Query string `json:"query"`
	}

	buf, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return skipAuth, err
	}

	body := ioutil.NopCloser(bytes.NewBuffer(buf))
	copyBody := ioutil.NopCloser(bytes.NewBuffer(buf))

	if err := json.NewDecoder(body).Decode(&req); err != nil {
		return skipAuth, err
	}
	r.Body = copyBody

	schema := graphqlExecutableSchema.Schema()
	parsedQuery, gerr := gqlparser.LoadQuery(schema, req.Query)
	if gerr != nil {
		return skipAuth, gerr
	}

	// Retrieve the directive argument value
loop:
	for _, op := range parsedQuery.Operations {
		for _, sel := range op.SelectionSet {
			if field, ok := sel.(*ast.Field); ok {
				objDefinition := field.ObjectDefinition
				if objDefinition == nil {
					skipAuth = false
					break loop
				}

				for _, f := range objDefinition.Fields {
                                         if field.Name != f.Name {
						continue
					}
					schemaQuery := schema.Query
					if schemaQuery != nil {
						if info := schema.Query.Fields.ForName(f.Name); info != nil {
							if skipAuthDirective := info.Directives.ForName("skipAuth"); skipAuthDirective != nil {
								skipAuth = true
								break loop
							}
						}
					}

					schemaMutation := schema.Mutation
					if schemaMutation != nil {
						if info := schema.Mutation.Fields.ForName(f.Name); info != nil {
							if skipAuthDirective := info.Directives.ForName("skipAuth"); skipAuthDirective != nil {
								skipAuth = true
								break loop
							}
						}
					}
				}
			}
		}
	}

	return skipAuth, nil
}
func Middleware(next http.Handler, graphqlExecutableSchema graphql.ExecutableSchema) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if ok, _ := skipAuth(graphqlExecutableSchema, r); ok {
			next.ServeHTTP(w, r)
			return
		}

		// other programs goes here
	})
}

TheDhejavu avatar Jun 01 '23 02:06 TheDhejavu