Closed Bug 1071247 Opened 5 years ago Closed 5 years ago

HTTP 401 - 110 INVALID_AUTH_TOKEN when accessing /fxa-oauth/params with an existing hawk session

Categories

(Hello (Loop) :: Server, defect)

defect
Not set

Tracking

(Not tracked)

VERIFIED FIXED

People

(Reporter: MattN, Assigned: alexis+bugs)

References

Details

(Whiteboard: [qa+])

Attachments

(1 file)

My understanding is that we agreed the other week that a logged-in FxA user should be able to request /fxa-oauth/params without an error message so that parameters can be retrieved again.

Currently I get HTTP 401 - 110 INVALID_AUTH_TOKEN and this breaks a user trying to login after a restart since we only persist the hawk token and not the other OAuth tokens until bug 1047133 is fixed. Even with that bug we'll still want to fetch the params fresh again (I was thinking of not persisting them).

I think I'll put a temporary hack in desktop for now to clear the FxA hawk token upon shutdown until bug 1047133 is fixed.
Whiteboard: [qa+]
Mattn, you cannot access /fxa-oauth/params with the anonymous hawk session.
You can only access /fxa-oauth/params with an oauth session that you get from this endpoint the first time.

Here I can reproduce what happens with an anonymous session

$ http POST https://loop.services.mozilla.com/registration simplePushURL=http://requestb.in/199uh4b1
HTTP/1.1 200 OK
Access-Control-Expose-Headers: Hawk-Session-Token
Connection: keep-alive
Content-Type: application/json
Date: Tue, 23 Sep 2014 07:55:26 GMT
Hawk-Session-Token: 0586d964673fd4f04a73efba16d3525423c36c49a52fa2368072292b96d1da84
Timestamp: 1411458926
Transfer-Encoding: chunked


$ http POST https://loop.services.mozilla.com/fxa-oauth/params --auth-type hawk --auth "0586d964673fd4f04a73efba16d3525423c36c49a52fa2368072292b96d1da84:"
HTTP/1.1 401 Unauthorized
Connection: keep-alive
Content-Length: 106
Content-Type: application/json; charset=utf-8
Date: Tue, 23 Sep 2014 07:55:54 GMT
Timestamp: 1411458954
WWW-Authenticate: Hawk error="Unknown credentials"

{
    "code": 401, 
    "errno": 110, 
    "error": {
        "error": "Unauthorized", 
        "message": "Unknown credentials", 
        "statusCode": 401
    }
}

Here is what happens with an Oauth session:

$ http POST https://loop.services.mozilla.com/fxa-oauth/params 
HTTP/1.1 200 OK
Access-Control-Expose-Headers: Hawk-Session-Token
Connection: keep-alive
Content-Length: 331
Content-Type: application/json; charset=utf-8
Date: Tue, 23 Sep 2014 07:56:46 GMT
Hawk-Session-Token: 9d3111c41a3a35ece08aec65b7200e2bc787cf6785b14213ae285de5f0ded9b5
Timestamp: 1411459006

{
    "client_id": "a8b39c2b1cab722e", 
    "content_uri": "https://accounts.firefox.com", 
    "oauth_uri": "https://oauth.accounts.firefox.com/v1", 
    "profile_uri": "https://profile.accounts.firefox.com/v1", 
    "redirect_uri": "urn:ietf:wg:oauth:2.0:fx:webchannel", 
    "scope": "profile", 
    "state": "fef6798629cd411292c094a21341366bd0d9de74849bc2627f75bc7592a9f58f"
}

$ http POST https://loop.services.mozilla.com/fxa-oauth/params --auth-type hawk --auth "9d3111c41a3a35ece08aec65b7200e2bc787cf6785b14213ae285de5f0ded9b5:"
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 331
Content-Type: application/json; charset=utf-8
Date: Tue, 23 Sep 2014 07:56:58 GMT
Server-Authorization: Hawk mac="/N7AuiT93J2ZuG+xTBx2GGy5dBWQiEMhfXydLYq4RTY="
Timestamp: 1411459018

{
    "client_id": "a8b39c2b1cab722e", 
    "content_uri": "https://accounts.firefox.com", 
    "oauth_uri": "https://oauth.accounts.firefox.com/v1", 
    "profile_uri": "https://profile.accounts.firefox.com/v1", 
    "redirect_uri": "urn:ietf:wg:oauth:2.0:fx:webchannel", 
    "scope": "profile", 
    "state": "fef6798629cd411292c094a21341366bd0d9de74849bc2627f75bc7592a9f58f"
}
The code is supposed to be using the FxA hawk session:
https://mxr.mozilla.org/mozilla-central/source/browser/components/loop/MozLoopService.jsm?rev=786a047a934f&mark=839-840#833

Can you test with a Firefox build that has bug 1065144 but not bug 1071259? You can see the HTTP requests in the Browser Console.
(In reply to Rémy Hubscher (:natim) from comment #1)
> Mattn, you cannot access /fxa-oauth/params with the anonymous hawk session.
> You can only access /fxa-oauth/params with an oauth session that you get
> from this endpoint the first time.

That's what I'm doing. It's not possible for the code to do this hawkRequest with the guest session.

I'm debugging this more on the client side.
Could you provide me the requests made to the server?

I double checked and the hawkSession are valid for a month of inactivity.
Each time they are used it is touch for one month more.
After investigation, we're not sure to understand what's the use case you're trying to follow here:

Currently what's supported by the server is:

- You create a hawk session for FxA, and then you upgrade it to a "connected" state, using the OAUth flow.

It seems that what you're doing is the following (correct me if incorrect):

1. Get a hawk session for OAuth (POST /fxa-oauth/params);
2. Ugrade it using FxA+OAuth; (POST /fxa-oauth/token);
3. Store and use the hawk session during the rest of the browser session (registration, call-url, etc.);

Then, when the browser restarts (that's when things start to bug):

4. With the persisted hawk session, connect to /fxa-oauth/params.

At this point, you're getting a 401. I don't really understand what you're trying to achieve here? 
Your hawk session is already tied to the FxA account, so there is no need to do the OAuth flow again (which is what seems to happen here).

*Do you want to check the validity of the OAuth token?*

In that case, we would have to think more about it. After a quick hack, if you get a new OAuth state, then you still need to enter your password to the FxA service.

It seems that what we want here is to have persistent session across restarts, and I believe this is provided by hawk already (so when opening again firefox, you're already connected).

*Do you want to sign with another user (using the same hawk session)?*

I hope not, but in that case you'll need to destroy your current hawk session and get a new one before upgrading it.
That being said, the fact you're receiving a 401 — 110, is due to the fact that we're testing the session is valid checking if there is a state associated to the user.

In case there is no state, you're given a 401, and once you've done the flow, there is no state associated to the user anymore. Depending what we want to achieve, we can make this return a 200 and work better, or we can change the way the client is behaving.
(In reply to Alexis Metaireau (:alexis) from comment #5)
> It seems that what you're doing is the following (correct me if incorrect):
> …

Yes, all of the above is correct.

> At this point, you're getting a 401. I don't really understand what you're
> trying to achieve here? 

I'm trying to get the various configuration parameters (mainly the profile_uri) so I can fetch the profile information. Having that also check that session is still valid at the same time is really nice too.

> Your hawk session is already tied to the FxA account, so there is no need to
> do the OAuth flow again (which is what seems to happen here).

IMO, getting the parameters doesn't necessarily mean I want to do the whole flow. Sometimes you simply want the parameters e.g. to see if they changed.

> *Do you want to check the validity of the OAuth token?*
> 
> In that case, we would have to think more about it. After a quick hack, if
> you get a new OAuth state, then you still need to enter your password to the
> FxA service.
> 
> It seems that what we want here is to have persistent session across
> restarts, and I believe this is provided by hawk already (so when opening
> again firefox, you're already connected).

Right but we should make sure that it's still valid in case the session expired and we need to be able to fetch the profile info again from the OAuth profile server and that requires knowing the URL of the server (which comes from the params request).

> *Do you want to sign with another user (using the same hawk session)?*
> 
> I hope not, but in that case you'll need to destroy your current hawk
> session and get a new one before upgrading it.

No, I never want to do that.
(In reply to Alexis Metaireau (:alexis) from comment #6)
> That being said, the fact you're receiving a 401 — 110, is due to the fact
> that we're testing the session is valid checking if there is a state
> associated to the user.

Why would you need to do that for /fxa-oauth/params? For the first request to that endpoint, you're creating a new state I thought.

> In case there is no state, you're given a 401, and once you've done the
> flow, there is no state associated to the user anymore. Depending what we
> want to achieve, we can make this return a 200 and work better, or we can
> change the way the client is behaving.

The 200 is what I'm hoping for. The endpoint is to get params and that's what I want to do. IMO there just happens to be a side effect that it creates a state and hawk token the first time it's accessed.
(In reply to Matthew N. [:MattN] from comment #8)
> I'm trying to get the various configuration parameters (mainly the
> profile_uri) so I can fetch the profile information. Having that also check
> that session is still valid at the same time is really nice too.

Ok, so we're doing that for two things: a. getting profile information and b. checking the session is still valid.

> IMO, getting the parameters doesn't necessarily mean I want to do the whole
> flow. Sometimes you simply want the parameters e.g. to see if they changed.

And this is key here: we shouldn't try to regenerate the state here if not needed (e.g. if we want a. only).

Would it work if there were a way to specifically ask to *not* genrate a new state here?

In case you want to check the validity of the OAuth token (because as you said that's something valid to do), I believe the FxA OAuth server should *not* ask you your credentials again in the webpage (but that's a different issue, I think).

I believe you can directly ask to the content server using your OAuth credentials. This way you should be able to get the (maybe updated) profile image and check at the same time the OAuth session is still valid.

> Right but we should make sure that it's still valid in case the session
> expired and we need to be able to fetch the profile info again from the
> OAuth profile server and that requires knowing the URL of the server (which
> comes from the params request).

This could be cached since it's not likely to change since the first request. In case of a 404 there, maybe we could do another call on the POST /fxa-oauth/params endpoint.

> Why would you need to do that for /fxa-oauth/params? For the first request
> to that endpoint, you're creating a new state I thought.

That's correct, but the state expires as soon as it's used, so you cannot reuse it.
This is to avoid letting an attacker discover what the state is by trying to use it many times.
 
> The 200 is what I'm hoping for. The endpoint is to get params and that's
> what I want to do. IMO there just happens to be a side effect that it
> creates a state and hawk token the first time it's accessed.

If the only thing you want to do here is to retrieve the params, then yes you should have a 200 here.
(In reply to Alexis Metaireau (:alexis) from comment #9)
> (In reply to Matthew N. [:MattN] from comment #8)
> > I'm trying to get the various configuration parameters (mainly the
> > profile_uri) so I can fetch the profile information. Having that also check
> > that session is still valid at the same time is really nice too.
> 
> Ok, so we're doing that for two things: a. getting profile information and
> b. checking the session is still valid.
> 
> > IMO, getting the parameters doesn't necessarily mean I want to do the whole
> > flow. Sometimes you simply want the parameters e.g. to see if they changed.
> 
> And this is key here: we shouldn't try to regenerate the state here if not
> needed (e.g. if we want a. only).
> 
> Would it work if there were a way to specifically ask to *not* genrate a new
> state here?

Yes, how about a GET request for that case if it can't be handled easily without that? The POST was only used because the state was getting created.

> In case you want to check the validity of the OAuth token (because as you
> said that's something valid to do), I believe the FxA OAuth server should
> *not* ask you your credentials again in the webpage (but that's a different
> issue, I think).
> 
> I believe you can directly ask to the content server using your OAuth
> credentials. This way you should be able to get the (maybe updated) profile
> image and check at the same time the OAuth session is still valid.

Right, that checks that the OAuth session is still valid but not that the FxA Loop Hawk session is valid which is what I was trying to do after a restart.

> > Right but we should make sure that it's still valid in case the session
> > expired and we need to be able to fetch the profile info again from the
> > OAuth profile server and that requires knowing the URL of the server (which
> > comes from the params request).
> 
> This could be cached since it's not likely to change since the first
> request. In case of a 404 there, maybe we could do another call on the POST
> /fxa-oauth/params endpoint.

Yes, that is an option but if I can avoid implementing a caching policy then I would prefer that :)

> > Why would you need to do that for /fxa-oauth/params? For the first request
> > to that endpoint, you're creating a new state I thought.
> 
> That's correct, but the state expires as soon as it's used, so you cannot
> reuse it.
> This is to avoid letting an attacker discover what the state is by trying to
> use it many times.
>  
> > The 200 is what I'm hoping for. The endpoint is to get params and that's
> > what I want to do. IMO there just happens to be a side effect that it
> > creates a state and hawk token the first time it's accessed.
> 
> If the only thing you want to do here is to retrieve the params, then yes
> you should have a 200 here.

Sounds good.
(In reply to Matthew N. [:MattN] from comment #10)
> Yes, how about a GET request for that case if it can't be handled easily
> without that? The POST was only used because the state was getting created.

+1.

> Right, that checks that the OAuth session is still valid but not that the
> FxA Loop Hawk session is valid which is what I was trying to do after a
> restart.

to check the hawk oauth fxa session is still valid, you just need to send a request to the server and it will tell you, no need to be proactive here I believe (e.g that will be the case on POST /registration for instance)

> > This could be cached since it's not likely to change since the first
> > request. In case of a 404 there, maybe we could do another call on the POST
> > /fxa-oauth/params endpoint.
> 
> Yes, that is an option but if I can avoid implementing a caching policy then
> I would prefer that :)

Same on the server, if we can reduce the load on the servers that would be super helpful (we'll already have a lot of load over there !)

I'm changing the server code to support GET on /params and to regenerate a new state each time we expire one.
Attached file link to github PR #213
Attachment #8494658 - Flags: review?(tarek)
Attachment #8494658 - Flags: feedback?(MattN+bmo)
Finally didn't went for the GET, but the result will stay almost the same.
Comment on attachment 8494658 [details] [review]
link to github PR #213

This works for me and I commented on the pull request.
Attachment #8494658 - Flags: feedback?(MattN+bmo) → feedback+
Assignee: nobody → alexis+bugs
Status: NEW → ASSIGNED
Attachment #8494658 - Flags: review?(tarek) → review+
https://github.com/mozilla-services/loop-server/commit/3eea0e46c85acbe1fc49ef20a7b7be519f081aee
Status: ASSIGNED → RESOLVED
Closed: 5 years ago
Resolution: --- → FIXED
Blocks: 1073141
Alexis, do you know when this patch will make it to the production server?
Flags: needinfo?(alexis+bugs)
I just filled a bug for this to be deployed on staging and then production. It usually takes about 4 days.

https://bugzilla.mozilla.org/show_bug.cgi?id=1076747
Flags: needinfo?(alexis+bugs)
This was deployed to production this morning (bug 1077777).
OK, so I guess this went out with 0.12.3
Status: RESOLVED → VERIFIED
You need to log in before you can comment on or make changes to this bug.