ory / kratos

The most scalable and customizable identity server on the market. Replace your Homegrown, Auth0, Okta, Firebase with better UX and DX. Has all the tablestakes: Passkeys, Social Sign In, Multi-Factor Auth, SMS, SAML, TOTP, and more. Written in Go, cloud native, headless, API-first. Available as a service on Ory Network and for self-hosters.
https://www.ory.sh/?utm_source=github&utm_medium=banner&utm_campaign=kratos
Apache License 2.0
11.33k stars 963 forks source link

New Ory Action: after login attempt #3580

Open aaronz-vipaso opened 1 year ago

aaronz-vipaso commented 1 year ago

Preflight checklist

Ory Network Project

No response

Describe your problem

We would like to hook in some custom logic during a login flow before the user-entered data is evaluated.

Currently, Kratos offers two hook positions during a login flow:

However, we would like to have an additional hook position in between those two.

A hook that is triggered with the user-entered data before it is processed and eventually becomes a successful login.

When I am talking about user-entered data, I mean identity-identifier/password credentials.

I imagine that the hook gets called with the identity identifier in its payload.


With such a hook, we could use our logic to prevent password brute force attacks.

Describe your ideal solution

A new hook during the login flow sends the used identity identifier (phone number/email) with the payload. The hook can interrupt the login flow.

(I would be happy to contribute this feature, as I have some time that I can allocate for open source.)

Workarounds or alternatives

idk

Version

1.0.0

Additional Context

No response

kmherrmann commented 1 year ago

Thanks for filing this, sounds entirely reasonable - we'd welcome a PR.

How exactly would you use that for brute force prevention?

aaronz-vipaso commented 1 year ago

How exactly would you use that for brute force prevention?

If we can get the identity identifier for each login attempt, we can maintain a counter in Redis (simple example) for each identifier. If we register too many or too fast one after the other login attempts for a specific identity identifier we could interrupt the login flow.

kmherrmann commented 1 year ago

How exactly would you use that for brute force prevention?

If we can get the identity identifier for each login attempt, we can maintain a counter in Redis (simple example) for each identifier. If we register too many or too fast one after the other login attempts for a specific identity identifier we could interrupt the login flow.

Understood. Are you self-hosting or using Ory Network? In Ory Network we have these kinds of counters already in place on our end and would have different solution options that don't require your own counters in Redis.

aaronz-vipaso commented 1 year ago

Understood. Are you self-hosting or using Ory Network? In Ory Network we have these kinds of counters already in place on our end and would have different solution options that don't require your own counters in Redis.

We are self-hosting it, as we need to be able to deploy on-prem.

aaronz-vipaso commented 1 year ago

@kmherrmann What naming would you suggest for the new action/hook? How could it fit into the current trigger precedence?

Maybe something like:

selfservice:
  flows:
    login:
      before:
        hooks:
          - hook: hook_1
      after_attempt:
        hooks:
          - hook: hook_a
      after: 
        hooks:
          - hook: hook_2
kmherrmann commented 1 year ago

i'd call it failed_attempt or after_failure. Semantically, after_attempt isn't as clear IMHO - i wouldn't know if it also triggers on successful login.

aeneasr commented 1 year ago

after_failure 👍

aaronz-vipaso commented 1 year ago

I was imagining a hook that is triggered after the user submits the data but before Kratos evaluates it.

So the hook could interrupt the flow (determined by the identity identifier) regardless if it would be successful or not.

But after_failure also makes sense and better fits the existing trigger model.

aaronz-vipaso commented 1 year ago

Well, but an after_failure hook alone would not satisfy the requirement to be able to interrupt a login flow. As it would be triggered only after Kratos has evaluated the user-entered login data.

graph LR
   classDef green fill:#2A7D36,color:#fff;
    BL(Before login) --> L{Login}
    L -->|on submit| AA(After attempt)
   AA ~~~|hook payload contains identity identifier <br> hook can interrupt flow| AA
    AA:::green --> AL(After login)
    AA --> AF(After failure)
    AL --> LPW(On password login)
    AL --> LOIDC(On social/OIDC login)
    AL --> LWA(On WebAuthn login)

@aeneasr @kmherrmann What name would you suggest instead of after_attempt?

kmherrmann commented 1 year ago

Well, but an after_failure hook alone would not satisfy the requirement to be able to interrupt a login flow. As it would be triggered only after Kratos has evaluated the user-entered login data.

@aeneasr @kmherrmann What name would you suggest instead of after_attempt?

So you want this to run before kratos validates the credentials? I'm wondering if the desired behavior can't be achieved by just using the after_login hook? Yes, Kratos would do the check, but in that webhook you could still interrupt the flow.

aaronz-vipaso commented 1 year ago

In my observation, when I configure an after_login webhook, it is only triggered after a successful login.

However, I would like to keep track of login attempts regardless of the success and eventually interrupt the login flow.

With such a hook, I could limit the login attempts for users in a specific time frame.

kmherrmann commented 1 year ago

I would like to bring your attention to https://github.com/ory/kratos/issues/3037 - here is a proposal to do this right in kratos, which may or may not be a better approach for this scenario.

If you want to proceed with the webhook route, i think you can either

atreya2011 commented 1 year ago

@aaronz-vipaso As @kmherrmann mentioned, I have proposed a design in #3037 and although progress is plodding, I am working on it. If you have proposals for improvement, let's work on this together. I am planning to put up a draft PR by the end of this year or hopefully by Jan 2024. Meanwhile, we can discuss points of improvement/addition either here or in the issue mentioned. What we need is a clear database design on how we will be handling the data payload passed to the hook.

BrandonNoad commented 1 year ago

introduce the after_failure webhook and integrate your throttle/block webhook on both events, after_failure and after_login

I think this is the best approach.

Although we use Ory Network and its rate limiting should provide similar protection that "locking" a user's account would, we are often asked on security questionnaires if our system can limit failed login attempts. So this webhook would allow us to check that box (at least until #3037 is ready).

davidspiess commented 1 year ago

We are also interested in this hook. Our use case is the migration to ory network from our previous identity provider, where we can't export the old password hashes. This hook would allow us to validate the entered user password with the old identity provider through an ROPC call and on success migrate his password to kratos.

aaronz-vipaso commented 1 year ago

@atreya2011 I don't see the addition of the two hooks after_submit and after_failure as a direct replacement for #3037. But the hooks could maybe be used to more easily integrate the logic needed for #3037.

graph LR
   classDef green fill:#2A7D36,color:#fff;
    BL(Before login) --> L{Login}
    L -->|on submit| AS(After submit)
   AS ~~~|check if login attempt should be evaluated <br> depending on the identity trying to login - <br> if attempt succeeds threshold: interrupt flow| AS
    AS:::green --> AL(After login)
    AL ~~~|reset failed login count of identity| AL
    AS --> AF(After failure):::green
    AF ~~~|increment failed login count of identity| AF
    AL --> LPW(On password login)
    AL --> LOIDC(On social/OIDC login)
    AL --> LWA(On WebAuthn login)
adpaste commented 2 months ago

We implemented the after_submit webhook in https://github.com/ory/kratos/pull/4077

We chose to implement after_submit because it allows us to rate-limit login attempts before checking the credentials.

barnarddt commented 2 months ago

Hi

We are also interested in the after_failure hook. This is to get some info into not just why our users are failing login but also get an idea of why there's a gap between the before_login and the after_login. Our thesis is a lot of social logins get abandoned rather than outright fail. The rate limiting and is also useful as we find some our execs accounts get targeted, but happy for Ory to do the more broad under attack kind of protection. Happy to add a PR for it as well