Kinde has a fun feature called workflows where you can bring your own code to the authentication experience. As a challenge/experiment/torture to learn how to write workflows, I wanted to extend the login and check the user's IP with a threat intelligence provider to block it if the abuse confidence score is too high. This article goes through what workflows are, how they can be extended with threat intelligence providers, and some lessons for myself on using AI to write this stuff.
While the example threat intelligence provider is AbuseIPDB and the code has been tailored for the service, any other security provider that has a REST API can be used. Same goes with the caching used later on, which in this example is Upstash's Redis service.
If you want to jump straight into it, grab the checkIPWithAbuseIPDBWorkflow.ts file from the Kinde's workflow examples repo.
Auth flow
The auth flow I had in mind was:
- User goes to login page that has a Google social login only
- User clicks on the Sign in with Google button and completes the Google auth part
- Workflow sends IP to AbuseIPDB API and receives an abuse confidence score
- Workflow allows the login to complete if the abuse confidence score is less than 90
Along the way of developing this workflow, I realised that it could get expensive to check every single login with AbuseIPDB API since they charge per check. So I decided to cache the results with a time-to-live (TTL) on the record so that IPs that have been seen before would be reused and then refreshed from time to time.
The auth flow then became:
- User goes to login page that has a Google social login only
- User clicks on the Sign in with Google button and completes the Google auth part
- Workflow checks if IP exists in an Upstash cache
- If IP exists in cache, then re-use existing abuse confidence score
- If IP does not exist in cache, then send IP to AbuseIPDB API and receives an abuse confidence score
- Workflow allows the login to complete if the abuse confidence score is less than 90
A dodgy diagram for illustration purposes:
What are Kinde workflows
To directly quote the documentation:
In Kinde, workflows exist to give you (almost) unlimited power over how Kinde works for you.
Develop code in your own repo, and push it to a Kinde workflow, where we execute it in ways that you define, triggered by Kinde events.
At the time of writing, the list of triggers available are:
- User post authentication
- M2M token generation
- Existing password provided
- Pre-MFA
- User token generation
- User pre-registration
- Plan selection
- Plan cancellation request
The one we’ll be targeting today is the User post authentication workflow since we’ll be checking if the IP address used during the authentication is malicious or not.
Each time the workflow is run, you can review the logs associated. The log output is dependent on what you log from the workflow using console.log, which can be very helpful for troubleshooting.
This workflow makes liberal use of environment variables. This way the workflow code itself doesn’t need any API keys, URLs for specific service endpoints, or score thresholds in the code. These can be kept securely within your Kinde business. This also gives you the flexibility to make some changes without having to update the code and redeploy the workflow. With ABUSEIPDB_BLOCK_THRESHOLD
, you configure the abuse confidence score that an IP address must be below to successfully log in.
Who is AbuseIPDB
I’m a big fan of AbuseIPDB since you can quickly check if an IP address has been reported for abusive or malicious traffic in their crowdsourced network. Whenever we’re dealing with suspicious connections in our application logs, I’ll often cross check the source IP to see if there’s an abuse score. If it comes back with something bad, the IP will go into our WAF’s block list.
Using their API, you can automate the checks and integrate their abuse confidence scores into your own application or WAF. The number of checks varies depending on your plan.
Also note that we’re only consuming the abuse confidence score from AbuseIPDB’s response. If you inspect the full payload, there’s a LOT of interesting data in there. For example, if you wanted to stop impossible travel, then you could cache the country code and then block the login if the there’s logins from different countries within a specified timeframe. That’s definitely a future post with a more complex workflow and is something some of our enterprise customers are already doing now with Kinde workflows.
Where is Upstash
As part of the workflow example, I wanted to cache the abuse confidence scores somewhere so that I wasn’t calling the AbuseIPDB so often. I used Upstash because it was a cloud based Redis service, which meant I didn’t need my own infrastructure, it would be super fast, and the key value storage would be easy to work with.
The record layout would be
- Key: abuseipdb:ip:{ip-address}
- Value: Abuse confidence score returned from AbuseIPDB, which is a 0 - 100 where 100 is guaranteed bad
And example of this for the IP 8.8.8.8 if AbuseIPDB returned a score of 30 would be
- Key: abuseipdb:ip:8.8.8.8
- Value: 30
This made the key somewhat predictable and the only piece of information being pulled out would be the abuse score. Upstash supports TTLs natively, so the key would automatically expire after the TTL is hit.
Note though, the way that the workflow has been written is that the function caching the abuse confidence score is somewhat generic. This means you can replace Upstash with something else that does caching. You’ll likely need to mess around with the code for a different service, but it doesn’t really matter as long as the cache function can pass around the abuse confidence scores.
The workflow code
Code for this and other examples are on Kinde's workflow examples repo.
Privacy concern
This workflow is sharing the IP address used in the login with both AbuseIPDB and Upstash. Sharing with AbuseIPDB is unavoidable since they are the primary intelligence provider. Sharing with Upstash could possibly be avoided by using a salted hash of the IP address or something directly hosted in your own environment. Unfortunately at the moment, the Kinde workflows environment doesn't support the crypto module and the AI had some wildly creative suggestions on how to create a hash that I didn't want to try out. So onto the company backlog about adding something that could assist with creating a good hash. I'll update the code and article when that comes around.
This part is important because if you operate a business in the EU or have EU customers, you'll may need to add these vendors to your sub-processors list since you're sharing the IP address with them and it may be considered personal information with respect to the GDPR. Check with your legal team :)
Challenges
I'm no software engineer, so I basically leaned on a mix of Gemini and Cursor to help get through this. Gemini was not so good with anything other than suggesting single line changes because it couldn't get over the tight restrictions imposed by the Kinde workflow environment. Cursor was much better, especially when provided with some other examples. Once I had a functioning workflow with just AbuseIPDB, life got a lot easier adding more features and adjusting the behaviour. A huge helper was pasting the output from the workflow runtime logs back in the AI chat because it would sometimes expose debug information to help it understand the environment that it's working in.
Back for another day
This will be a bit of a let down... Since this post is getting way too long and my brain hurts from writing, I’m going to dump out the high level steps needed to get this setup in Kinde and create a walkthrough post later with some screenshots. This will give me a chance to setup a fresh business and repo to make sure this all goes super smooth for anyone else trying to replicate this.
- Have functioning Kinde business, probably using a starter kit
- Clone Github repo with workflow templates
- Remove the workflows not being used from the cloned repo
- Connect your Github to Kinde
- Select the repo with the workflow
- Create an AbuseIPDB account and get an API key
- Create an Upstash account and get the API keys
- Create all the environment variables in Kinde
- Test a login to make sure everything works end to end
- Review log output
Top comments (0)