Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authentication with graphql data provider issue #4830

Closed
arrrrny opened this issue May 19, 2020 · 18 comments
Closed

Authentication with graphql data provider issue #4830

arrrrny opened this issue May 19, 2020 · 18 comments

Comments

@arrrrny
Copy link

arrrrny commented May 19, 2020

I am using react-admin with simple graphql dataprovider. On backend I block all requests without token except my login endpoint. I use authProvider to make a request to login backend. However React-admin still use dataprovider on authentication page and since I don't send an authentication token in the header before login I get an error.

Expected behavior should be: React-admin not using graphql dataprovider while loading login page.

Screen Shot 2020-05-19 at 4 02 04 PM

If I allow unauthenticated requests to /graphql in the backend, I successfully see the login page.
Screen Shot 2020-05-19 at 3 41 56 PM

As you see, react-admin is making a request to data-provider while loading the login page. But this is an issue since grahpql provide has only one endpoint /graphql

Here is the code that encounters the issue:

        const httpLink = createHttpLink({ uri: 'http://localhost:8080/graphql' })

        const authLink = setContext((_, { headers }) => {
            const token = Cookies.get('accessToken')

            return {
                headers: {
                    ...headers,
                    authorization: `Bearer ${token}`
                }
            }
        })

        const client = new ApolloClient({
            link: authLink.concat(httpLink),
            cache: new InMemoryCache()
        })

        buildGraphQLProvider({
            client
        })
            .then(dataProvider => this.setState({ dataProvider }));

So react-admin graphql dataprovider needs to access /graphql to load successfully, but that is the only endpoint for all other queries as well. I need to secure that endpoint.

@arrrrny arrrrny changed the title Authentication with graphql provider issue Authentication with graphql data provider issue May 19, 2020
@wmwart
Copy link

wmwart commented May 20, 2020

Some other problem on the other side. My client looks the same, but it works:

import { ApolloClient } from 'apollo-client';
import { createHttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory';
import { setContext } from 'apollo-link-context';
import { getAccessToken } from './getAccessToken';
import { URI_AUTH } from '../appsettings';

const httpLink = createHttpLink({
  uri: URI_AUTH
});

const authLink = setContext(async (_, { headers }) => {           
    
  const token = await getAccessToken(); 

  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : "",
    }
  }
});


export const apolloAuthClient = new ApolloClient({
  link: httpLink,
  cache: new InMemoryCache(),
});

export const apolloAuthClientWithAuth = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache(),
});

@arrrrny
Copy link
Author

arrrrny commented May 20, 2020

@wmwart how does your backend look like? Do you block /graphql for unauthenticated requests?
Here is my dirty and ugly fix for the problem

       const httpLink = createHttpLink({ uri: 'http://localhost:8080/graphql' })

        const authLink = setContext((_, { headers }) => {
            const token = Cookies.get('accessToken')

            return {
                headers: {
                    ...headers,
                    authorization: `Bearer ${token}`
                }
            }
        })

        const client = new ApolloClient({
            link: authLink.concat(httpLink),
            cache: new InMemoryCache()
        })

        try {
            

        const dataProvider= await buildGraphQLProvider({
                client
            })
            this.setState({ dataProvider })
        } catch (error) {
        console.log(error)    
        Cookies.remove('accessToken');
        window.location.replace("/")
        }

I created react-admin inside another app and in the "/" I do login separate from react-admin.

@wmwart
Copy link

wmwart commented May 20, 2020

what do you mean block /graphql for unauthenticated requests?
On all requests or mutations that require authentication, I use the withAuth directive, which returns an error if there is no token or it is not valid. In general, this is not a react-admin issue. Something with the logic of your application.

@arrrrny
Copy link
Author

arrrrny commented May 20, 2020

Below is the middleware that I use, all pages except /subscriptions/login page responds with 401, if there is not a Auth token in the header, which means, if there is a request to /graphql without a token, I respond with 401. However, react-admin login page still make a request to /graphql with graphql-data-provider. If it was a REST Api with different endpoints This would not be an issue. Issue is react-admin grahpql data provider makes a request to /graphql while loading login page. which it shouldn't make this request.What is the withAuth directive? Can you show me your code?

  use(req: Request, res: Response, next: Function) {


    const authJsonWebToken = req.headers.authorization;
    if (req.baseUrl !== '/subscriptions/login') {


      if (!authJsonWebToken) {
        console.log('invalid')
        throw new HttpException('Invalid Token', HttpStatus.UNAUTHORIZED)
      } else {
        try {
          const store = jwt.verify(authJsonWebToken.slice(7, authJsonWebToken.length), 'mySecretKey');
          if (store) {
            req['store'] = store;
            next();
          }
        } catch (error) {
          throw new HttpException('Invalid Token', HttpStatus.UNAUTHORIZED)
        }
      }
    }
    else {
      next();
    }
  }
}```

@djhi
Copy link
Collaborator

djhi commented Jun 4, 2020

However React-admin still use dataprovider on authentication page

I don't get this. The login form uses the login hook which does not call the dataProvider but the authProvider.

Besides, so far, none of the graphql packages deal with authentication.

I think this is more an how to question than an actual bug in react-admin and I invite you to look for answers on StackOverflow.

@djhi djhi closed this as completed Jun 4, 2020
@arrrrny
Copy link
Author

arrrrny commented Jun 4, 2020

Well thats the expected behavior, however, it is making a request to /graphql data provider while loading the login page. This is before user putting credentials, this happens while loading the login page. For security reasons if I block all requests to my /graphql endpoint without access token, react admin crashes. This is an Actual Bug

@arrrrny
Copy link
Author

arrrrny commented Jun 4, 2020

Please install a graphql data provider and go to login page, watch your Network tab and you will see that it makes a get request (probably getting schema) to /graphql

@djhi
Copy link
Collaborator

djhi commented Jun 4, 2020

Ah yes, you're right. Not sure about what we can do here but I'll mark this as an enhancement. However, please note that our graphql dataProviders are mostly just examples. Use them as a starting point to implement your own.

@djhi djhi reopened this Jun 4, 2020
@arrrrny
Copy link
Author

arrrrny commented Jun 4, 2020

I did a workaround to encapsulate react-admin around another react app and everything works fine.

@djhi
Copy link
Collaborator

djhi commented Jun 4, 2020

That's one way to do it but I think fixing it in the provider itself would probably be better. Postponing the introspection call until the first real dataProvider call (on a resource) for example

@arrrrny
Copy link
Author

arrrrny commented Jun 4, 2020

That would be ideal

@praseetha-nair
Copy link

@arrrrny Did you find any solution for this issue. I am also having the same problem. Any help would be appreciated.

@greatwitenorth
Copy link

@djhi How about just allowing a null dataProvider if authProvider is provided and the user isn't currently logged in? This way we could just initialize and set the dataProvider in the login() method.

@BowlingX
Copy link
Contributor

Instead of using the introspection query, you can also pass the introspection result directly when you create the provider. If your server blocks access to the schema without authentication, it makes sense to generate the introspection result ahead of time.

@BowlingX
Copy link
Contributor

You can use graphql codegen (https://graphql-code-generator.com/) for that

.codegen.yml

overwrite: true
schema: 'http://localhost:4000/graphql'
generates:
  ./your/path/introspection.json:
    plugins:
      - introspection

Then import the introspection.json file in your project and initialize the provider with the schema:
See https://github.com/marmelab/react-admin/tree/master/packages/ra-data-graphql#introspection-options

@valoricDe
Copy link

Could someone share how your authProviders using apollo for your graphql endpoint looks like?

@djhi
Copy link
Collaborator

djhi commented Oct 4, 2021

This should be fixed by #6628 as we now send the introspection query only when the dataProvider actually make a request

@djhi djhi closed this as completed Oct 4, 2021
@Myou5uf
Copy link

Myou5uf commented Oct 25, 2023

@arrrrny Hi. Could someone please share what your authentication provider looks like using apollo for your graphql endpoint? I'm also trying to implement, but I don't understand yet how it should all look like

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants