LawJolla / prisma-auth0-example

Boilerplate Prisma Startup
MIT License
198 stars 19 forks source link

Authorization error #2

Closed kjetilge closed 6 years ago

kjetilge commented 6 years ago

After adding http://localhost:8000 to "Allowed Web Origins" in the Auth0 client console I was able to test login, but then I get:

auth.js:51:
{error: "access_denied", errorDescription: "Invalid URI "undefined/users/YXV0aDB8NWE4OWI1OTMzYWZmNzMxMDEzMjUxNzY5.json?auth=undefined"", state: "5p8LD.2Jl27.2uD1ot6wdwAqCNX0j5Bq"}
LawJolla commented 6 years ago

That's an authorization error from Auth0. There's something not properly set up in your Auth0 console. It looks like you're using the /users audience API instead of its own API. Please see the Readme of this project for more info. Hopefully that'll set things straight!

kjetilge commented 6 years ago

You where right - totally wrong auth0 setup. Now I've fixed that, but then I get:

auth result {status: "success", accessToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5rUk…fH7l7-Q7dOdBhxqPhC7LME1FwbOK1EDXGQ1FbZLr-6ci7imYA", idToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5rUk…HrWhPYu4Z87NIPsANa7-NF8OHpabF3FcZwixQ_IAzyq7gpDVg", expiresAt: "1519095808073"}

2localhost/:1 POST http://localhost:4000/ 401 (Unauthorized)
callback:1 Failed to load http://localhost:4000/: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8000' is therefore not allowed access. The response had HTTP status code 401. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

I've tried to set cors options like this in index.js:

server.options.cors = {origin: 'http://localhost:8000'}

But that's not working.

LawJolla commented 6 years ago

@kjetilge It's not a CORS error. When middleware interrupts the return and a CORS header isn't sent, so you'll always see that following the actual error, the 401 unauthorized.

Your real error is the 401 unauthorized. Double check that your auth header is set correctly (access token, not the id token) and that the .well-known JWT check.

By logging around in the checkJwt callback, you should be able to figure it out.

kjetilge commented 6 years ago

I'm still not having success with this. I can't seem to 'log around in the checkJwt callback' - how do I do That ?. Anyway I completed the Vuejs 2 Authentication Tutorial found here: https://auth0.com/blog/vuejs2-authentication-tutorial/ and everything went smooth. (I'm trying to adapt your example to Vuejs)

So I followed the same pattern when setting up a new API for you example. When I login I get:

auth result {status: "success", accessToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5rUk…DkknVB6u48Qltm8DJKr3K5Cz9VegNJil-Qf2iPn7K9uCqE1ow", idToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5rUk…E0vaUaROH4qVAFejLvUHIyvvaxZQNsKPXpc-gqn5-S6bWMSvw", expiresAt: "1519498335520"}accessToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5rUkNRMFEzUVVaQ1FqSkRSVGcxUmprNFF6Z3dPVU01TURRM04wUXhRVFF3TkRKRk9VTkdOQSJ9.eyJpc3MiOiJodHRwczovL2ZhZ2ZpbG0uZXUuYXV0aDAuY29tLyIsInN1YiI6ImF1dGgwfDVhOTE5M2ZkNDU3ZWNiNjc5YzVkZjliMSIsImF1ZCI6WyJodHRwczovL2V4YW1wbGUuY29tLyIsImh0dHBzOi8vZmFnZmlsbS5ldS5hdXRoMC5jb20vdXNlcmluZm8iXSwiaWF0IjoxNTE5NDkxMTM0LCJleHAiOjE1MTk0OTgzMzQsImF6cCI6InJUUmxUV3lzaGlEcDdjTGY5SElZM0U0aER6d0xZYU5jIiwic2NvcGUiOiJvcGVuaWQgcHJvZmlsZSBlbWFpbCJ9.Egn2RzBqw12BnWc9T-5mWZBqspB6OmD1KkzaigCrucgiAz8f3IjlABqblWJz_tEJ1QjzzwMA7Vd4GfjfR3MyNIKK9ASBIh6QNMHpjitHIaEYTcpbAT-6e1n4xrrxqoEx6xb_8tRxq9kwTN54APfeyADO0YpApcfeyFj7IXpZndIzjEYCR995Kl4TRBT4FhGFZlc6h0hRovb3f5G1nqfSVgUpUYMDdpflYRIis5oFjUt790xXNuAudc-X3ZgNR89o_XJA1Qvv4jivoYPndRPxhrkKjVRosUSml7EeDDkknVB6u48Qltm8DJKr3K5Cz9VegNJil-Qf2iPn7K9uCqE1ow"expiresAt: "1519498335520"idToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5rUkNRMFEzUVVaQ1FqSkRSVGcxUmprNFF6Z3dPVU01TURRM04wUXhRVFF3TkRKRk9VTkdOQSJ9.eyJuaWNrbmFtZSI6ImtqZXRpbCIsIm5hbWUiOiJramV0aWxAZ2VpcmJvLm5vIiwicGljdHVyZSI6Imh0dHBzOi8vcy5ncmF2YXRhci5jb20vYXZhdGFyLzUzODk0YWM5NTQ0OTdjMjBjZTIyMzZmMjIzOTVmNDA4P3M9NDgwJnI9cGcmZD1odHRwcyUzQSUyRiUyRmNkbi5hdXRoMC5jb20lMkZhdmF0YXJzJTJGa2oucG5nIiwidXBkYXRlZF9hdCI6IjIwMTgtMDItMjRUMTY6NTI6MTIuODU1WiIsImVtYWlsIjoia2pldGlsQGdlaXJiby5ubyIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiaXNzIjoiaHR0cHM6Ly9mYWdmaWxtLmV1LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHw1YTkxOTNmZDQ1N2VjYjY3OWM1ZGY5YjEiLCJhdWQiOiJyVFJsVFd5c2hpRHA3Y0xmOUhJWTNFNGhEendMWWFOYyIsImlhdCI6MTUxOTQ5MTEzNCwiZXhwIjoxNTE5NTI3MTM0LCJhdF9oYXNoIjoiMW84dGprMm9rS0FxcXF6QXlJZTVnUSIsIm5vbmNlIjoiUkNIbjRTZVRtYmJEQVVOTVUxREVhT3dSZzhPM0N4OU4ifQ.t0_39Kda0Hs54-OGUGm6MBZjPcN0QheYTauskSAqiPfjG-HdFRN4IrTQ0m88PsEXcwxkH6UTTY1VPxjKpsyGragrM19Cq0-fhbRr_jPmUC6WTgfv2B3ulL1QjM05K1fICji4Jjp3s5Zwu6C9zjeV2w4ubLiCIkQv58JELVDi1t_Vu70ZhHnCmfeWnCDie5Ifyv_Ae_nVcrjAA-7ORwksb6FXvLp3V8-NeOopF89DF1WhFVaMy360assHO0DnL0-1aOkAswrGwc4tFPbGWoaoTpQo4uelg0GZTpqXAE0vaUaROH4qVAFejLvUHIyvvaxZQNsKPXpc-gqn5-S6bWMSvw"status: "success"__proto__: Object
auth.js:79 ApolloClient {defaultOptions: {…}, resetStoreCallbacks: Array(0), link: ApolloLink, cache: InMemoryCache, store: DataStore, …}
index.js:47 auth result {status: "success", accessToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5rUk…DkknVB6u48Qltm8DJKr3K5Cz9VegNJil-Qf2iPn7K9uCqE1ow", idToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5rUk…E0vaUaROH4qVAFejLvUHIyvvaxZQNsKPXpc-gqn5-S6bWMSvw", expiresAt: "1519498335530"}
2index.js:32 access_token eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5rUkNRMFEzUVVaQ1FqSkRSVGcxUmprNFF6Z3dPVU01TURRM04wUXhRVFF3TkRKRk9VTkdOQSJ9.eyJpc3MiOiJodHRwczovL2ZhZ2ZpbG0uZXUuYXV0aDAuY29tLyIsInN1YiI6ImF1dGgwfDVhOTE5M2ZkNDU3ZWNiNjc5YzVkZjliMSIsImF1ZCI6WyJodHRwczovL2V4YW1wbGUuY29tLyIsImh0dHBzOi8vZmFnZmlsbS5ldS5hdXRoMC5jb20vdXNlcmluZm8iXSwiaWF0IjoxNTE5NDkxMTM0LCJleHAiOjE1MTk0OTgzMzQsImF6cCI6InJUUmxUV3lzaGlEcDdjTGY5SElZM0U0aER6d0xZYU5jIiwic2NvcGUiOiJvcGVuaWQgcHJvZmlsZSBlbWFpbCJ9.Egn2RzBqw12BnWc9T-5mWZBqspB6OmD1KkzaigCrucgiAz8f3IjlABqblWJz_tEJ1QjzzwMA7Vd4GfjfR3MyNIKK9ASBIh6QNMHpjitHIaEYTcpbAT-6e1n4xrrxqoEx6xb_8tRxq9kwTN54APfeyADO0YpApcfeyFj7IXpZndIzjEYCR995Kl4TRBT4FhGFZlc6h0hRovb3f5G1nqfSVgUpUYMDdpflYRIis5oFjUt790xXNuAudc-X3ZgNR89o_XJA1Qvv4jivoYPndRPxhrkKjVRosUSml7EeDDkknVB6u48Qltm8DJKr3K5Cz9VegNJil-Qf2iPn7K9uCqE1ow
2localhost/:1 POST http://localhost:4000/ 401 (Unauthorized)
callback:1 Failed to load http://localhost:4000/: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8000' is therefore not allowed access. The response had HTTP status code 401. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

As you can see the access_token is set, so I'm logged in, but when the client tries to access the server using the token I'm denied.

My .env file:

PRISMA_ENDPOINT="https://eu1.prisma.sh/public-orchidtwister-894/test/dev"
PRISMA_SECRET="myapp123"
AUTH0_DOMAIN="fagfilm.eu.auth0.com"
AUTH0_AUDIENCE="https://example.com"
AUTH0_ISSUER="https://fagfilm.eu.auth0.com/"

My auth0-variables:

  api_audience: 'https://example.com/',
  domain: 'fagfilm.eu.auth0.com',
  clientId: 'rTRlTWyshiDp7cLf9HIY3E4hDzwLYaNc',
  callbackUrl: 'http://localhost:8000/callback'

Can you see any obvious errors here ?

The Vue example configures the express server with cors: app.use(cors());

I still suspect it might be an issue as explained here: https://blog.graph.cool/enabling-cors-for-express-graphql-apollo-server-1ef999bfb38d

The error that they show in the example looks very similar to mine.

except for some lines with console log, the code is untouched.

LawJolla commented 6 years ago

Your AUTH0_AUDIENCE isn't right. The audience is your client API (configurable on the Auth0 console). For instance, mine is: "https://wheelk.auth0.com/api/v2/"

Also, are you using RS256 for your Auth0 API? If that's not set, it won't work.

And great work on the Vue example!

kjetilge commented 6 years ago

screetshot

The AUTH0_AUDIENCE is confusing, on my API settings page it states it's "https://example.com/" And it worked in the VueJs tutorial - very strange I'm using RS256.

kjetilge commented 6 years ago

I tried to change the AUTH0_AUDIENCE to something that looks like yours ( domain + api/v2 ) Then I get:

{error: "access_denied", errorDescription: "Service not found: https://fagfilm.eu.auth0.com/api/v2", state: "z-jVvMnooEXa3NUuBeqkK3on2OGnMx95"}
index.js:47 auth result {status: "error", errMessage: "access_denied"}

That suggests to me that "https://example.com/" at least is recognised as a service

LawJolla commented 6 years ago

You need to add the API. (I forget how to do this, but it should be apparent in the docs). Your API screen should look like this.

image 2018-02-24 at 10 26 26 am
kjetilge commented 6 years ago

I did: skjermbilde 2018-02-24 kl 18 28 11

kjetilge commented 6 years ago

Here it is: skjermbilde 2018-02-24 kl 18 28 58

LawJolla commented 6 years ago

I wish I knew Auth0 better to advise you on this one. I'm pretty sure it needs the xxx.auth0.com/api signature.

kjetilge commented 6 years ago

It says in the manual that I can give the identifier any name. The url format is just a recommendation. Here's the client details: skjermbilde 2018-02-24 kl 18 30 14

kjetilge commented 6 years ago

the client quickstart auth0 class looks like this:

export default class Auth {
  auth0 = new auth0.WebAuth({
    domain: 'fagfilm.eu.auth0.com',
    clientID: 'rTRlTWyshiDp7cLf9HIY3E4hDzwLYaNc',
    redirectUri: 'http://localhost:3000/callback',
    audience: 'https://fagfilm.eu.auth0.com/userinfo',
    responseType: 'token id_token',
    scope: 'openid'
  });

  login() {
    this.auth0.authorize();
  }
}

Here the audience paramater is something completely different.

and the server quickstart jwtCheck like this:

var jwtCheck = jwt({
    secret: jwks.expressJwtSecret({
        cache: true,
        rateLimit: true,
        jwksRequestsPerMinute: 5,
        jwksUri: "https://fagfilm.eu.auth0.com/.well-known/jwks.json"
    }),
    audience: 'https://example.com/',
    issuer: "https://fagfilm.eu.auth0.com/",
    algorithms: ['RS256']
});
LawJolla commented 6 years ago

That's your problem and why the token is bad. Note from the readme, the client audience and server audience must be the same.

kjetilge commented 6 years ago

Yes, but this is not my code, it's AUTH0 quickstart code. I did use the same audience on both: 'https://example.com/'. After making the Api I clicked the "None Interactive Clients" link and changed that client to 'interactive' - just as in the vuejs tutorial and I used the identifier as audience, and it worked!

Exactly where in the dashboard did you find your audience ? or is there a particular way to figure it out ?

LawJolla commented 6 years ago

Part of the reason I made this demo was to learn Auth0.. so this is the blind leading the blind. 🙂

I went to console -> APIs. What I did to get that API there, I don't remember.

Perhaps what I need to do is start this project from scratch and document my Auth0 steps.

But making that change allowed the server to verify the token?

kjetilge commented 6 years ago

Are you using your Auth0 Management API as api in this example ? I can se that the audience for my Auth0 Management API is "https://fagfilm.eu.auth0.com/api/v2/" witch is on the same form as my "https://wheelk.auth0.com/api/v2/". Should the Auth0 Management API be used for this ?

LawJolla commented 6 years ago

The API needs to be used because /userinfo doesn't return an access token, just an id token.

kjetilge commented 6 years ago

Good News and bad news

The good news is I've got it to work! The bad news is I have no idea why! 😆

What I did was to open the default Auth0 Management API and create a none interactive test client for it and then open this client and change it to an SPA client. Then I just plotted in the parameters in the .end file and auth0-variables as described in his repos 'Getting started' section and everything suddenly works!

What I did before was to first create a new API and then create an SPA client for it the same way as above.

What's the difference here ? The identifier name ? Must it end with '/api/v2/' ?

kjetilge commented 6 years ago

Oops.. did not see your comment above before posting. Thanks for clearing up the /userinfo thing.

kjetilge commented 6 years ago

Looks like this behaviour perhaps may be changed in "API Authorization Settings" under Tenant Settings -> General.

LawJolla commented 6 years ago

I should have mentioned in the readme that it requires the SPA auth flow. Sorry for not making that clearer!

And I misspoke about /userinfo. It returns a string access token. But the API returns a JWT access token. Why the difference.. I have no idea. 🙂

kjetilge commented 6 years ago

I used SPA from the beginning, the big problem was using a custom API. Only the default system API should be used it seems.