Handling INVALID_CREDENTIALS (password change blocking macaroon refresh)

I’ve been trying to track down the cause of the following gnome-software bug:

The following error message is being returned by snapd from a non-login API call:

cannot authenticate to snap store: Provided email/password is not correct.

It looks like the final Provided email/password is not correct part is from the store, and judging by the tests this would be accompanied by the INVALID_CREDENTIALS code. From what I’ve discovered, I believe this is the code path that leads to the error:

  1. client makes an API call to snapd providing a valid snapd macaroon for authentication.
  2. to process the API call, snapd makes a request to the store using the store macaroon corresponding to the client’s snapd macaroon.
  3. store responds with a 401 response with WWW-Authenticate
  4. snapd calls Store.refreshUser, which attempts to refresh the discharges on that store macaroon.
  5. store detects that the user has changed their password since the original macaroon was issued and responds with the Provided email/password is not correct error
  6. snapd forwards that error on to the client

From what I can tell, the cannot authenticate to snap store: prefix is added when snapd is forwarding a generic error from the store:

Since it is seen as a generic error gnome-software can’t do anything more useful than present it to the user, which is a bit unsatisfying.

So part of the solution would be to pass this error through in a way that gnome-software / snapd-glib can recognise. I can put together a PR for that easy enough. But there is still the open question of how this error should be handled exactly.

My first thought was to just throw out the stored snapd macaroon and start over. But this has the effect of leaving behind an non-viable snapd user ID. Will this lead to snapd making spurious calls to the store when refreshing snaps? What would happen to purchased snaps made using the old snapd login?

From a look through the snapd source, it looks like the right solution might be to call /v2/login again with the existing snapd macaroon. It looks like this should associate a new store macaroon with the existing snapd login. Does that sound about right?

1 Like

I’ve put together a PR for the snapd side of this:

I’m still interested in knowing what the preferred way to refresh a snapd user in this state is. Is calling /v2/login with the existing snapd macaroon and new store credentials the right option?

I’ve been working on the gnome-software side of this feature. One part of this is the following PR for snapd-glib:

Previously the library was omitting an Authorization header when calling /v2/login, even if the client object was configured with a macaroon. This change just gets rid of that special casing so snapd can update the existing snapd user via this code path:

My only concern is the other source of snapd authentication problems: if the snapd macaroon itself is invalid. This might happen if the user copies their home directory to another machine, for instance.

Things seem to work in this case because failures to validate the request’s macaroon are ignored and treated the same as no credentials are provided:

But the TODO comment has me a bit worried: am I relying on undefined behaviour here, or should we be able to rely on a call to /v2/login with an invalid snapd macaroon but valid store credentials succeeding?

Yes, it should be the right option. If the local user is correctly authenticated, the respective store credentials will be updated.

Well, AFAICT, the idea is that eventually invalid snapd credentials won’t allow to continue after that check. I wouldn’t rely on this behavior unless the plan has changed (note I’m really part of the store team but I was involved in the initial client implementation).

Out of curiosity, is this a case we would like to work? I mean, should snapd local users be copyable/movable between machines? In any case, if the snapd local macaroon is invalid, and the TODO plan is implemented, you will probably be getting an error.

Copying the home directory between machines (or NFS mounted home directories will almost certainly not work. If I decode my snapd macaroon, the contents is:

location snapd
identifier 1
signature $secret

The identifier 1 part and the key used to produce the signature both depend on snapd’s internal state, which will be different on the new machine.

My main point is that when snapd returns an “Unauthorized” error to a request accompanied by a macaroon, there are two possible causes: either the snapd macaroon is bad, or the associated store macaroon is bad. To handle this, one of the following needs to be true:

  1. snapd needs to tell the client which case happened.
  2. the recovery method for each error case needs to be the same.

If we can do (2), then life will be easier for the client. Especially if new failure cases arise in future.