Skip to content
This repository has been archived by the owner on Jun 23, 2021. It is now read-only.

Missing transaction_id in validate/check request #15

Closed
cornelinux opened this issue Dec 19, 2018 · 13 comments
Closed

Missing transaction_id in validate/check request #15

cornelinux opened this issue Dec 19, 2018 · 13 comments

Comments

@cornelinux
Copy link
Contributor

cornelinux commented Dec 19, 2018

The trigger challenge call [1] [2 ] returns a transaction_id that is used to match the response (OTP value), that the user enters, to the created challenge.

Currently the response of the triggerchallenge is not evaluated - the transaction_id response value is not used.

I recommend reading the transaction_id and passing it to the second step, so that the transaction_id can be used in the second validate/check request.

The parameter could be passed as a hidden value in the loginform.

If the parameter transaction_id is not sent, the database table challenges will be filled up with challenges that will not get deleted. Also this might lead to unexpected results.

@sbidy sbidy self-assigned this Dec 19, 2018
@sbidy sbidy added the bug label Dec 19, 2018
@sbidy
Copy link
Owner

sbidy commented Dec 19, 2018

Mh - I added a really "dirty hack" for that

if (transaction_id.Length > 20) return transaction_id.Remove(20);

One question: Regading the API doc. you wrote "All challenge response tokens have the same transaction_id in this case." But i have tested it with a non-challenge response (TOTP) and a mail token. So how can I determine the right transaction_id from the transaction_ids list?
In my opinion I have to send the correct transaction_id for the corresponding token in the validation request.

@cornelinux
Copy link
Contributor Author

If the user has a non-challenge-Token like TOTP and a challenge-Token like email, this is usually a bad idea (especially when not using PINs).
In this case the admin should configure the TOTP token in the backend to act as challenge-token.

Then for both tokens a challenge would be generated. I think they have the same transaction_ids.
The user then can either enter the OTP from his TOTP token or from the Mail. The plugin should send this one transaction_id togeather with the entered OTP value.

privacyIDEA will sort it out, which token/challenge matches and then also remove the challenges from the challenge table.

@sbidy
Copy link
Owner

sbidy commented Dec 21, 2018

Ok I tested all use cases and the "challenge+non-challenge" seems to be the only faulty one.
But (in my test enviroment) I get two different "transaction_ids".

How can I handle this unusual use case? Removing the all numbers after index 20 seems to me not the best way 😄 ... So do you have any suggestions? Can I send booth transaction_ids to the privacyIDEA like an JSON array ("transaction_id": [transaction_id#1, transaction_id#2])?

@sbidy
Copy link
Owner

sbidy commented Dec 21, 2018

So one more ...

  1. I defined a challenge-response user in the config.
  2. A user tries to log on with a non-challenge response token (e.g. TOTP) and a PIN
  3. triggerChallenge is called and the privacyIDEA returns a transaction_id(s):
{
   "jsonrpc":"2.0",
   "signature":"18855 [...] 72293765594368",
   "detail":{
      "transaction_ids":[
         "10807740570876518271"
      ],
      "messages":[
         "please enter otp: "
      ],
      "threadid":140076638963456
   },
   "versionnumber":"2.21",
   "version":"privacyIDEA 2.21",
   "result":{
      "status":true,
      "value":1
   },
   "time":1545390429.625696,
   "id":1
}

3.1 in the response is value = 1. So this indicates (see doc.) that the user has a challenge token assigned. But I've only added one TOTP token for that user (is this an error by my testing environment?).
4. Now the getAuthOTP is called and send a request to the privacyIDEA:

"pass", PIN+OTP,
"realm", realm,
"transaction_id", 10807740570876518271
  1. But the privacyIDEA returns:
    {"jsonrpc": "2.0", "signature": "4318283192983[...]6195204", "versionnumber": "2.21", "version": "privacyIDEA 2.21", "result": {"status": true, "value": false}, "time": 1545390782.642436, "id": 1}

If I remove the "transaction_id" header from the request it works?!

Should I determine If the token is a challenge token or not? I think you mention some similar at #2?
Why the privacyIDEA doesn't ignore the transaction id?

@cornelinux
Copy link
Contributor Author

3.1 The TOTP token acts like a challenge response token, if you set the policy scope=auth, action=challenge_response.

4 and 5: If you add a transaction_id privacyidea assumes, that you want to answer a challenge. In the case of SMS; email, totp, the challenge is only answered with the OTP value.
You are providing "PIN+OTP", which does not match the OTP value of the token.

So you either need to omit the "PIN" or you need ot omit the "transaction_id".

As a matter of fact with TOTP tokens the authentication works without the transaction_id.
This is because it is no cryptographically challenge, only the sending of a value. But the database table challenge is never cleaned up. AND: there are other tokens, that actually use some challenge texts / cryptographic challenges.

To recap: You can trigger a challenge either as a user by

/validate/check?user=...&pass=PIN

or as and administrator by

/validate/triggerchallenge?user=....

In the case of ADFS it makes sense to use triggerchallenge. You do not want the user to use another PIN, since he already specified the LDAP password.

@cornelinux
Copy link
Contributor Author

cornelinux commented Dec 21, 2018

So what you want to send to the privacyIDEA server in case there are two challenge response tokens.
I AM VERY SORRY! Our API actually sucks - or at least has an inconsistancy.

I described it here:
privacyidea/privacyidea#1353

So it is up to you, if you

  1. use only the first transaction_id or
  2. iterate through all transaction_ids.

At least in version 3.0 we will also have a JSON element detail->transaction_id, that has a transaction_id, that works for all challenges.

@sbidy
Copy link
Owner

sbidy commented Dec 21, 2018

Cool, thank you! 😎
We are using the (unusual 😉 ) AD-Acoount + PIN + OTP combination because we have implemented the Token (without AD-Creds.) some other additional endpoints.
Multiple PINs and passwords = multi-factor 🤣

So to recap both issues:

  1. I implement a workaround for the triggerchallenge with the PIN+TOTP combi: If the response from the triggerChallenge function contains value = 1, the getAuthOTP will be send two request to the API - one with the transaction_id and one without. If both returning a false - the auth fails. If one of the request returns a true - the auth. succeeds.

  2. The provider sends only the first transaction_id to the privacyIDEA. Maybe I do some pre-work for the upcoming 3.0 API. 😉

@cornelinux
Copy link
Contributor Author

In regards to 1:

I would suggest to make it configurable:

a) Add transaction_id in response = True: The transaction_id will be added in the response, I.e. no PIN is given.
b) Add transaction_id in response = False: The transaction_id is missing in the response, => this will work with PIN.

Thus you would only need to send one request.
But in case of b) you still fill up the challenge table.

In regards to 2:

You would currently still have the problem with two challenge response tokens.

@cornelinux
Copy link
Contributor Author

I already pushed a fix privacyidea/privacyidea@b110e7f
so that you only would need to check for detail->transaction_id.
Multiple challenges will have the same one transaction_id.

sbidy added a commit that referenced this issue Dec 21, 2018
… page). Regards to #15 : send two requests to the privacyIDEA.
@sbidy
Copy link
Owner

sbidy commented Dec 21, 2018

Regards to 2.: Yes, I'll go fix this in the next release. But now both should have the same id❓

Regards to 1:
Now I send two request if the value is >0 in the challenge request return (in my opinion HOTP, TOTP, SMS and E-Mail are all challenge tokens = the value is allays >0). I have to support both use cases: with and without a configured PIN.

if (getJsonNode(responseString, "value") != "0") this.isChallengeToken = true;

and the two requests:

public bool getAuthOTP(string OTPuser, string OTPpin, string realm, string transaction_id)

But the transactions in the challenge table are not purged - if a PIN+OTP combination is used - with this workaround. My suggestion is, that the API provides the capability to purge the open challenge by call in the admin context something like a DELETE request to /token/challenge?id=<trasaction_id>. So I can purge the pending challenges after the auth is done. Really "unusual way" but only needed if a PIN is used.

@cornelinux
Copy link
Contributor Author

Regards to 2.: Yes, I'll go fix this in the next release. But now both should have the same id

This is currently in a PR. I am not sure when this will be in a release. But yes, in the long run both will have the same ID.

But the transactions in the challenge table are not purged - if a PIN+OTP combination is used - with this workaround. My suggestion is, that the API provides the capability to purge the open challenge by call in the admin context something like a DELETE request to /token/challenge?id=<trasaction_id>. So I can purge the pending challenges after the auth is done. Really "unusual way" but only needed if a PIN is used.

I see the requirement. I am not sure, if a new API call would make sense.
The challenge table however also holds the serial. https://github.com/privacyidea/privacyidea/blob/master/privacyidea/models.py#L1161
We could add a parameter to the /validate/check request like purge_challenges=1.
If the authentication with (of for) a certain serial number succeeds this parameter could indicate, that challenges for this very token should be purged.

@cornelinux
Copy link
Contributor Author

I moved the PR that harmonizes the transaction_id to a intermediate milestone.
https://github.com/privacyidea/privacyidea/milestones/2.23.4%20Maintenance

So we should be able to release it somewhere in January. So yes, there will be only one transaction_id.

The response looks like mentioned in privacyidea/privacyidea#1353

    "detail": {
        "attributes": {
            "state": null,
            "valid_until": "2018-12-21 13:50:07.834025"
        },
        "message": "please enter otp: , Enter the OTP from the Email:",
        "messages": [
            "please enter otp: ",
            "Enter the OTP from the Email:"
        ],
        "multi_challenge": [
            {
                "attributes": null,
                "serial": "TOTP0001573B",
                "transaction_id": "01928502550899427322"
            },
            {
                "attributes": {
                    "state": "01928502550899427322",
                    "valid_until": "2018-12-21 13:49:37.107654"
                },
                "serial": "PIEM000024B0",
                "transaction_id": "01928502550899427322"
            },
        "threadid": 139947024250624,
        "transaction_ids": [
            "01391359713621908840",
            "01391359713621908840"
        ],
        "transaction_id": "01928502550899427322"
    },

@cornelinux
Copy link
Contributor Author

@sbidy: The current releases of privacyIDEA (2.23.5 and 3.0.1) now provide this single transaction_id in the top level json root. So I guess this can be closed.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants