Authenticating with Rails API from React

133 views Asked by At

I have a Rails API that serves as the back-end of my application. I use Devise and Doorkeeper. Whenever I try to authenticate a user from React (which is being used as my front-end) it fails.

loginSlice.js

export const loginUser = createAsyncThunk('currentUser/loginUser', async (email, password) => {
  const response = await fetch(`${client.BASE_URL}:${client.PORT}${client.USER_AUTH_PATH}`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      grant_type: client.grant_type,
      email,
      password,
      client_id: client.client_id,
      client_secret: client.client_secret,
    }),
  });

  const data = await response.json();

  return data;
});

( I am importing the client, I just wanted to keep the code as minimal as possible in my question )

What I'm getting back is

{
  "error": "invalid_grant",
  "error_description": "The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client."
}

I am using a grant_type of password (which I'm expecting in the back-end), so that's not the problem. I'm also using the proper client_id and client_secret. Also I'm making the request to the proper path - otherwise I wouldn't recieve the error message from the server, nor would it show the following in my log (see next).

This is what Rails log file is showing me:

Started POST "/oauth/token" for ::1 at 2023-04-14 17:28:02 +0300
Processing by Doorkeeper::TokensController#create as */*
  Parameters: {"grant_type"=>"password", "email"=>"[email protected]", "password"=>"[FILTERED]", "client_id"=>"theClientID", "client_secret"=>"[FILTERED]", "token"=>{"grant_type"=>"password", "email"=>"[email protected]", "password"=>"[FILTERED]", "client_id"=>"theClientID", "client_secret"=>"[FILTERED]"}}
  [1m[36mDoorkeeper::Application Load (2.1ms)[0m  [1m[34mSELECT "oauth_applications".* FROM "oauth_applications" WHERE "oauth_applications"."uid" = $1 LIMIT $2[0m  [["uid", "theClientID"], ["LIMIT", 1]]
  [1m[36mUser Load (0.9ms)[0m  [1m[34mSELECT "users".* FROM "users" WHERE "users"."email" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m  [["email", "[email protected]"], ["LIMIT", 1]]
  ↳ app/models/user.rb:18:in `authenticate!'
Completed 400 Bad Request in 619ms (Views: 0.2ms | ActiveRecord: 139.5ms | Allocations: 4627)

However, I created an index.html file and used the exact same function I'm using in my redux thunk and I do get back the authentication token as expected.

index.html

<html>
<head>
    <title>Fetcher</title>
</head>
<body>
    <script>
        async function connectUser() {
          fetch('http://localhost:3000/oauth/token', {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json'
            },
            body: JSON.stringify({
              grant_type: 'password',
              email: '[email protected]',
              password: 'somePassword',
              client_id: 'theClientID',
              client_secret: 'theClientSecret'
            })
          })
          .then(response => response.json())
          .then(data => console.log(data))
          .catch(error => console.error(error))
        }
        
        connectUser()
    </script>
</body>
</html>

When running this I get the following response:

{
  "access_token": "AlXoQgKLPh_sLO6EJfkN5Ld0534zYdG6o4-uOOyuZn0",
  "created_at": 1681462632,
  "expires_in": 7199,
  "refresh_token": "fNAwvF0NUNu_GL27s7jkjUuPXw-Tw_0cdGvi1eL-RjM",
  "token_type": "Bearer"
}

And my Rails log shows the following:

Started POST "/oauth/token" for ::1 at 2023-04-14 11:57:07 +0300
Processing by Doorkeeper::TokensController#create as */*
  Parameters: {"grant_type"=>"password", "email"=>"[email protected]", "password"=>"[FILTERED]", "client_id"=>"theClientID", "client_secret"=>"[FILTERED]", "token"=>{"grant_type"=>"password", "email"=>"[email protected]", "password"=>"[FILTERED]", "client_id"=>"theClientID", "client_secret"=>"[FILTERED]"}}
  [1m[36mDoorkeeper::Application Load (1387.4ms)[0m  [1m[34mSELECT "oauth_applications".* FROM "oauth_applications" WHERE "oauth_applications"."uid" = $1 LIMIT $2[0m  [["uid", "theClientID"], ["LIMIT", 1]]
  [1m[36mUser Load (0.6ms)[0m  [1m[34mSELECT "users".* FROM "users" WHERE "users"."email" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m  [["email", "[email protected]"], ["LIMIT", 1]]
  ↳ app/models/user.rb:18:in `authenticate!'
  [1m[36mTRANSACTION (69.8ms)[0m  [1m[35mBEGIN[0m
  [1m[36mDoorkeeper::AccessToken Exists? (312.1ms)[0m  [1m[34mSELECT 1 AS one FROM "oauth_access_tokens" WHERE "oauth_access_tokens"."token" = $1 LIMIT $2[0m  [["token", "[FILTERED]"], ["LIMIT", 1]]
  [1m[36mDoorkeeper::AccessToken Exists? (1.5ms)[0m  [1m[34mSELECT 1 AS one FROM "oauth_access_tokens" WHERE "oauth_access_tokens"."refresh_token" = $1 LIMIT $2[0m  [["refresh_token", "[FILTERED]"], ["LIMIT", 1]]
  [1m[36mDoorkeeper::AccessToken Create (164.9ms)[0m  [1m[32mINSERT INTO "oauth_access_tokens" ("resource_owner_id", "application_id", "token", "refresh_token", "expires_in", "scopes", "created_at", "revoked_at", "previous_refresh_token") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING "id"[0m  [["resource_owner_id", 1], ["application_id", 1], ["token", "[FILTERED]"], ["refresh_token", "[FILTERED]"], ["expires_in", 7200], ["scopes", ""], ["created_at", "2023-04-14 08:57:12.037562"], ["revoked_at", nil], ["previous_refresh_token", "[FILTERED]"]]
  [1m[36mTRANSACTION (69.0ms)[0m  [1m[35mCOMMIT[0m
Completed 200 OK in 3637ms (Views: 0.3ms | ActiveRecord: 2005.4ms | Allocations: 7056)

I enabled CORS on the server through rack-cors, I am running the Rails server on port 3000 and the React application on port 3001 (if that makes any difference). I have no more ideas about how this can happen and I would gratefully appreciate help. Thank you!

0

There are 0 answers