SpoonX / aurelia-authentication

Authentication plugin for aurelia.
http://aurelia-authentication.spoonx.org
MIT License
90 stars 60 forks source link

Getting authentication error message from server response JSON #332

Closed indfnzo closed 7 years ago

indfnzo commented 7 years ago

(maybe) related - https://github.com/SpoonX/aurelia-api/issues/134

Hi!

So my server returns a JSON response message to authentication requests so I can identify what error specifically happened and display that in my view (user doesn't exist, password doesn't match, field empty, etc).

{
    "success": false,
    "message": "User doesn't exist."
}

However, I can't access these response messages from my VM.

return this.authService.login(this.email, this.password)
    .catch(error => {
        console.log(error);
    });

All I get is a generic "Token not found in response" error. Is there any official way of getting this response or at least any workaround? Thanks!

RWOverdijk commented 7 years ago

@jemhuntr Hey, thanks for the question.

It's not related, no, but I've posted an answer on that issue again. :)

The "token not found" error you mention gets thrown when it, well, can't find the token on the response. (see here and here).

It tries to set the response object because your server is returning a valid response code, and authentication tries to access your token (here).

TL;DR; All you have to do to fix this, is fix your server to return the correct status code (401) so that the authentication library can throw an error and you can catch it as expected. Then it won't be a Token not found in response error.

Does this answer your question? :)

indfnzo commented 7 years ago

@RWOverdijk Thanks much! I got my setup working by fixing the server, although a console warning keeps popping up.

From what I read, I would have to reconfigure the fetch-client which is being used by aurelia-api, which is in turn being used by aurelia-authentication. I tried directly modifying the config for my authentication endpoint on my aurelia-api declaration inside main.js, but it seems it's still rejecting error responses.

aurelia.use
    .standardConfiguration()
    .feature("core")

    // configure aurelia-api
    .plugin("aurelia-api", config => {
        config.registerEndpoint("auth", configure => {
            configure.withDefaults({ credentials: "same-origin" });
        });
    })
    /* -- more plugins -- */

If I remove .standardConfiguration() right after aurelia.use, my app stops working completely. this is apparently the global FrameworkConfig, not the config for the HttpClient itself.

RWOverdijk commented 7 years ago

@jemhuntr I'm not sure I follow, what do you mean? I use this all the time and never get warnings.

indfnzo commented 7 years ago

I'm guessing that the rejectErrorResponses() method on the HttpClientConfiguration is causing this issue.

Since my server is now sending a 401 (instead of a 2xx response) on failed logins, aurelia-fetch-client is rejecting the response messages on these calls.

I can't seem to figure out how to configure aurelia-authentication or aurelia-api or aurelia-fetch-client to prevent it from rejecting error responses.

indfnzo commented 7 years ago

I'll try rebuilding my server from scratch.

delebash commented 7 years ago

I am having a similar problem.

After calling authservice.login('invalid-credentials')

I get an error response:

body:ReadableStream
bodyUsed:false
headers:Headers
ok:false
status:401
statusText:""
type:"cors"
url:"https://api.test.com/api/v2/user/session"

It is my understanding that an error is thrown because the session token is missing which is correct, but I can't figure out how to access the actual json response object returned from my server.

Below is what is returned from url:"https://api.test.com/api/v2/user/session" that I need to get access to.

{"error":{"context":null,"message":"Invalid credentials supplied.","code":401}}

Thanks

delebash commented 7 years ago

I got, just needed to call .json on the err object. Thanks

RWOverdijk commented 7 years ago

Awesome! Glad you got it.

reft commented 7 years ago

@RWOverdijk Hey, I get a console error if I return a 401 Unauthorized, (everything is working tho). Anyway to suppress this error?

reft commented 7 years ago

http://stackoverflow.com/questions/42532109/aurelia-auth-custom-response-message-console-error

RWOverdijk commented 7 years ago

@reft The console error is for the developer to know why. What are you looking for?

reft commented 7 years ago

@RWOverdijk If I return a json result with a message and an empty token I get:

login failure: Error: Token not found in response

As I understand I get this error because I have not returned an invalid response message.. BUT.. Can I read my json message from the server here? I dont want to return a response message because that generates a console error.

reft commented 7 years ago

Currently I have to put a generic message here:

    return this.authService.login(this.email, this.password)
        .then(response => {
            console.log("success logged " + response);
        })
        .catch(err => {
            ALERT('GENERIC MESSAGE');

            //err returns: "Token not found in response"
        });

"Token not found in response" message is found in file: authentication.js. There really should be support for returning a server message instead :(

indfnzo commented 7 years ago

@reft you need to call .json() on the err object you're getting from .catch(). Try this:


return this.authService.login(this.email, this.password)
    .then(response => {
        // do stuff
    })
    .catch(response => {
        if (response instanceof Error) {
            // server returned no response
            this.errorMessage = "Unable to contact authentication server.";
        } else {
            // server handled the response gracefully,
            // but since it's an error code we got, we're expecting
            // an error message from the server along with the error code
            response.json().then(error => {
                // `error` in this scope would be the JSON
                // that your server has sent back from your request
                console.log(error);
                this.errorMessage = error.message;
            });
        }
    });

Take note that for this to work, your authentication server has to respond with a non-success error code (401, for example).

Until now, there still is an ominous console warning I'm getting even if everything is working as expected in this setup. Tell me if you get that warning too: Warning: a promise was rejected with a non-error: [object Response]

delebash commented 7 years ago

I also had a problem with .json as I was unfamiliar with the fetch api. The .json is part of the new fetch api specifically the body mixin. Also, Warning: a promise was rejected with a non-error: [object Response] this is a bluebird warning. I also get this error message when my server sends a 401 response. You can turn all warnings off but I am also interested in finding the correct solution besides disabling warnings as per below

Promise.config(Object { warnings: boolean=false, longStackTraces: boolean=false, cancellation: boolean=false, monitoring: boolean=false }

Oh, just ran across this regarding the aurelia-fetch-client. Note: the user Bergi says it can be ignored, he seems to know what he is talking about if you look at his profile.

delebash commented 7 years ago

@jemhuntr As you mentioned the Warning: a promise was rejected with a non-error: [object Response] seems to be a result of config.rejectErrorResponses() in the aurelia-fetch-client under the Helpers section. I can see how to disable it in aurelia-fetch-client but not in the spoonx aurelia-api. Did you figure this out?

RWOverdijk commented 7 years ago

I think I see the problem. The reason it throws that error is because it can't find the token on the response. What we can do, perhaps, is add the server response to the error. Would that solve your issues?

If there's a token, it won't complain. But if the status code is a valid one, it will look for the token.

indfnzo commented 7 years ago

I can live with simply ignoring the warning, assuming that won't show up in production.

Although being able to access the server response from the error object would be a great addition.

RWOverdijk commented 7 years ago

@jemhuntr Awesome middle ground I'd say. Do you want to give a go at implementing this?

indfnzo commented 7 years ago

I'm a little busy with schoolwork, but I'll try to look at it over the weekend.

indfnzo commented 7 years ago

For anyone still having this issue, pull from the latest release and make sure your server returns a response code 200 along with the errors it generated.


return this.authService.login(this.email, this.password)
    .then(response => {
        // do something
    })
    .catch(error => {
        // The entire server response object is now directly accessible via
        // the error object's `responseObject` key, given that the server
        // responded with a 200 status code and aurelia-authentication
        // didn't find a token from the server response.
        console.log(error.responseObject);

        // If authService returned a response object from the server,
        // use that for outputting error message on the UI.
        // Otherwise, use the error as is (access error.message).
        this.error = error.responseObject || error;
    });