View Issue Details

IDProjectCategoryView StatusLast Update
0010172Talermerchant backendpublic2025-12-21 20:46
Reportersebasjm Assigned ToChristian Grothoff Resolution deadline
ImplementerReviewer 
Release signed byDeployed by 
VerifierCommunicator 
Data protection authority notification requiredNo  
CSIRT notification requiredNo  
Estimated effort (hours)Actual effort (hours)0 
Fast-trackNo
Work packagenone
PrioritynormalSeverityminorReproducibilityhave not tried
Status closedResolutionfixed 
Product Version1.0 
Target Version1.3Fixed in Version1.3 
Summary0010172: should not be able to create to create tokens with a token
DescriptionThis works, and I can use the token returned to create more tokens (on any scope)

$ curl 'http://merchant.taler.test/private/token' -H 'Authorization: Bearer secret-token:GVP8MZ2R22XGNY1KA9Q72RVMHZZ0E7K35E924ZJQECJTT0TFQ6NG' --data-raw '{"scope":"all","duration":{"d_us":"forever"}}'
{
  "access_token": "secret-token:H6QTCSWAD2458EDG09J3JCH98N63ARM0SCXKG4EM3PMP6W942CP0",
  "token": "secret-token:H6QTCSWAD2458EDG09J3JCH98N63ARM0SCXKG4EM3PMP6W942CP0",
  "scope": "all",
  "refreshable": false,
  "expiration": {
    "t_s": "never"
  }
}

The problem is that this is the normal token used by the merchant SPA. I think we should either have a special scope for tokens to create new tokens (1) or only allow the creation of tokens with Basic, not Bearer (2).

I think (2) is the simplest and safest.

We can also go for (1) for a "spa-session" scope. This allows the full management (like all) but prevents the creation of new tokens if leaked.
Tagssecurity

Required notifications

Address or linkRemarkAdded byNotifiedActions

Relationships

related to 0010575 implementingschanzen Enhance documentation for Authentication 
related to 0010639 closedsebasjm hide refresh option on ac token creation 

Activities

schanzen

2025-07-09 16:07

administrator   ~0025469

You should not be able to create a token with a broader scope than the one you present. I also though it had to be refreshable. Need to check the code.

schanzen

2025-07-09 16:09

administrator   ~0025470

What is the "normal token used by the SPA"? Because we do not have a scope for that, so I guess you already have a scope with "all". So your description would not be correct. We can define a special scope for SPAs, if you can provide me with a clear requirement and set of permissions required.

schanzen

2025-07-09 16:11

administrator   ~0025471

(Note: "all" always was and still is defined as "can do anything including refresh")

schanzen

2025-07-09 16:12

administrator   ~0025472

We cannot limit the token endpoint to only Basic, because that is what ":refreshable" is about.

schanzen

2025-07-09 16:12

administrator   ~0025473

Your proposal (1) also does not work, because you can request scope "spa-session:refeshable" and get a token that can be refreshed.

Christian Grothoff

2025-07-09 18:37

manager   ~0025474

The SPA clearly should ask for a non-refreshable token, possibly with a lifetime restriction that the user can configure on login.

When the SPA is used to create *another* token for export, I think it makes sense for the SPA to require the user to re-enter username/password for this.

Maybe this is a pure SPA issue and not something that requires changes in the backend?

sebasjm

2025-07-10 02:23

developer   ~0025479

1) the only way to create a new session should be using username and password (or even 2fa)
2) the refresh mechanism re-create the __same__ token (not a new one, in fact, refreshing should invalidate the previous one)

This way the user can keep control of the number of open sessions for an account.
I have shown a way to overcome this two restriction, having a token I can create a new token without user/password and a token without refresh.

a) do we want to have a scope that allow user of the token to create new tokens? (like the "all" scope) (consider that the user of the token may be a script or third party)

I think this is wrong, I don't see any reason why this would be a good idea and the merchant could potentially find an unreasonable amount of new open sessions.

b) how can we fix this?

I proposed 2 ways:
- only allow the creation of tokens using usr/pwd, the refreshing explained in (2) should care about the rest.
- modify the scope definition to exclude token creation permission or to ask for it if needed (again I don't see why but you sound like you have a reason)

If you insist on having a scope that can create tokens, then we need a scope that have "all - creation_of_tokens" __by definition__ since the SPA backoffice should have access to all API but the UI will ask (and test) user password before creating a new token.


> The SPA clearly should ask for a non-refreshable token, possibly with a lifetime restriction that the user can configure on login.

The spa will always ask for refreshable and max duration, this allows the backend server to impose the expiration (spa always check expiration independently of duration). It needs to be refreshable so the session only ends with the user hitting "log out" (or app close for too long)
Asking for non-refreshable token will make the SPA prompt for "enter the username and password again" before expiration (manually refresh) this is worse UX.

> When the SPA is used to create *another* token for export, I think it makes sense for the SPA to require the user to re-enter username/password for this.

The creation of new token is working as you describe. SPA ask for password (and test it) even if not required right now (that's why I'm creating this issue against backend). Password is always thrown away after used, the SPA never ever saves the password.

> You should not be able to create a token with a broader scope than the one you present

Off course, this is not the case. In the example I have started the issue I'm asking for a token (H6QT) with refreshable:false using the token (GVP8). I succeded to create a new token without user/password (using token GVP8 and also using token H6QT) with a token which is not refreshable which makes refreshable property useless. I can also use H6QT again in the same way.

> What is the "normal token used by the SPA"?

A token with scope "all", since the spa session should have access to all endpoints no other scope make sense.

schanzen

2025-07-10 05:53

administrator   ~0025480

The token with scope all is always refreshable. That is actually a bug: The "refreshable" field is set incorrectly in this special case and the refreshability is not indicated.

You claim (1) is still unsubstantiated and everything hinges on the argument why this is a requirement. Why should this not be possible? See also https://oauth.net/2/refresh-tokens/ on how this is a common pattern.

If you mean that the token refresh should invalidate an old token I agree. That would also be a new requirement an behaviour, but I can see why it makes sense.


Tokens are also not sessions. Maybe that is the case for the SPA but a token is simply a token.

The bigger problem is that we are mixing access tokens from login (session tokens) with third party access tokens. Those are actually thwo different things. Maybe this requires a bigger refactor: Separate login tokens (/private/token) from third party tokens (/private/third_party/token)

I think in general we are using terms here in the wrong way. Like access token, session and refresh.

Christian Grothoff

2025-07-10 07:25

manager   ~0025485

Ok, I'm also not sure that we need this, schanzen's arguments here seem quite reasonable. Shall we "wontfix" it?

Florian Dold

2025-08-04 20:29

manager   ~0025620

@schanzen: So the difference between login tokens and third party tokens would just be that third party tokens can't create other (third party / login) tokens? Is there a reason why we'd need separate endpoints or could we model it with scopes?

schanzen

2025-08-06 08:38

administrator   ~0025631

You can use a non-refreshable token as a third party token. But you have to be very careful that it is not refreshable. Otherwise, you cannot revoke it sanely, because the third party can create a lot of tokens that are not bound to it (you do not know who is the subject ob the token).

Christian Grothoff

2025-08-28 08:36

manager   ~0025704

Summary:
- SPA creates at login a refreshable token with scope "all"
- SPA can use this to create other tokens of all other scopes;
- if your token is refreshable, you can create tokens with lifetimes that
  exceed the lifetime of the current token (but not the scope of the current token)
- Usually (by default in the SPA) the (likely) 3rd party tokens will be non-refreshable
- For PoS app tokens and E-commerce frontends, this still works nicely if you use a non-refreshable token of
  infinite lifetime (which we permit in the SPA and the backend)

Assuming the above is done, I propose the following:

TODO:
- revoking a refreshable token can be tricky, as there could have been many tokens spawned
  from the original token; so we build a dependency tree where a token that has been
  created via refresh has a "parent" field pointing to the original token it was refreshed from;
  if a parent token is revoked (not merely expired), we DELETE it and then ON DELETE CASCADE
  should revoke all dependents;
- we'll need to write some slightly more funky GC routine to collect
  non-revoked refresh-chains of expired tokens

schanzen

2025-08-28 08:45

administrator   ~0025705

IF the UI forces non-refresh for 3rd party tokens, I do not think we need this.
We may want to delete old tokens upon refresh in any case however.

schanzen

2025-08-28 08:46

administrator   ~0025706

Instead, we may want to have a "bearer" subject associated with the token in order to revoke all tokens for an entity

schanzen

2025-08-28 09:23

administrator   ~0025709

Talked with Christian and we came up with the following proposal:

- When authenticating with ID/password (+2FA) a refreshable token may be requested (SPA use case)
- A refreshable token can be used to refresh its own token (SPA use case)
- 3rd party tokens may be created refreshable, but it is HIGHLY DISCOURAGED. Any admin may shoot himself in the foot if required. The UI should hide this or make it clear that it is dangerous.

Required changes:
- Merchant API: None
- UI: Hide option refreshable for token creation / or give stern warning
- Doc: Document behaviour for token endpoint.

schanzen

2025-11-25 09:59

administrator   ~0026613

I added a notification banne when refreshable tokens are selected. For the documentation improvements we have 0010575 to track now. Proposing to mark this resolved.

Issue History

Date ModifiedUsernameFieldChange
2025-07-09 14:55sebasjmNew Issue
2025-07-09 15:03Christian GrothoffAssigned To => schanzen
2025-07-09 15:03Christian GrothoffStatusnew => assigned
2025-07-09 16:07schanzenNote Added: 0025469
2025-07-09 16:09schanzenNote Added: 0025470
2025-07-09 16:11schanzenNote Added: 0025471
2025-07-09 16:12schanzenNote Added: 0025472
2025-07-09 16:12schanzenNote Added: 0025473
2025-07-09 18:37Christian GrothoffNote Added: 0025474
2025-07-09 18:37Christian GrothoffAssigned Toschanzen => sebasjm
2025-07-09 18:37Christian GrothoffStatusassigned => feedback
2025-07-10 02:23sebasjmNote Added: 0025479
2025-07-10 05:53schanzenNote Added: 0025480
2025-07-10 07:25Christian GrothoffNote Added: 0025485
2025-07-10 07:25Christian GrothoffAssigned Tosebasjm => Florian Dold
2025-08-04 20:28Florian DoldTarget Version => 1.6
2025-08-04 20:29Florian DoldNote Added: 0025620
2025-08-04 20:29Florian DoldAssigned ToFlorian Dold => schanzen
2025-08-06 08:38schanzenNote Added: 0025631
2025-08-28 08:36Christian GrothoffNote Added: 0025704
2025-08-28 08:36Christian GrothoffStatusfeedback => assigned
2025-08-28 08:45schanzenNote Added: 0025705
2025-08-28 08:46schanzenNote Added: 0025706
2025-08-28 09:23schanzenNote Added: 0025709
2025-08-31 17:28Christian GrothoffTag Attached: security
2025-08-31 17:33Christian GrothoffSeveritymajor => minor
2025-08-31 17:33Christian GrothoffProduct Version => 1.0
2025-08-31 17:33Christian GrothoffTarget Version1.6 => post-1.0
2025-11-18 21:05StefanRelationship addedrelated to 0010630
2025-11-20 11:00sebasjmRelationship addedrelated to 0010639
2025-11-25 09:57schanzen
2025-11-25 09:58schanzenRelationship addedrelated to 0010575
2025-11-25 09:59schanzenAssigned Toschanzen => Christian Grothoff
2025-11-25 09:59schanzenStatusassigned => feedback
2025-11-25 09:59schanzenNote Added: 0026613
2025-11-25 10:34Christian GrothoffStatusfeedback => informing
2025-11-25 10:34Christian GrothoffResolutionopen => fixed
2025-11-25 10:34Christian GrothoffFixed in Version => 1.3
2025-11-25 10:34Christian GrothoffTarget Versionpost-1.0 => 1.3
2025-12-03 14:07vecirexRelationship replacedparent of 0010630
2025-12-03 14:09vecirexRelationship replacedchild of 0010630
2025-12-11 23:53vecirexRelationship deletedchild of 0010630
2025-12-21 20:46Christian GrothoffStatusinforming => closed