How to validate password - PasswordNotAcceptable

What I’m trying to do:

I want to test if a password is valid before calling anvil.users.signup_with_email

What I’ve tried and what’s not working:

I’ve gone through Anvil Docs | anvil.users and don’t see a anvil.users.validate_password() function. Our registration process requires 2 steps:

  1. Auto-register the user on a 3rd party API and once successful,
  2. Complete the signup on the anvil app with anvil.users.signup_with_email.

We’re seeing failures in the 2nd step as Anvil is more stringent than the 3rd party API. We’d like to catch the password issue before the first step.

Is there a validate password function in Anvil, if so, how do we use it?

I just looked, but I haven’t seen one.

We’d like to catch the password issue before the first step.

In that case, renumber step 2 as step 0. You can use try with catch to detect failures.

If a step can fail, be prepared to roll back all prior steps. A Transaction would be nice, but that wouldn’t work across multiple databases (Anvil and 3rd party), so it has to be simulated with explicit code.

Thanks for your quick response. Pity there’s no function.

We need to do the steps in the above order as we don’t want to send a confirmation email and then the API has a reason for not registering.

I thought of the try catch scenario but since we’re using email confirmation, what email address do we use? Is there a way to switch off that off temporarily so we can test the password? Or is there a ‘null’ email address we could use? If we set it to an invalid email address, that may ‘solve’ it but then you’ll likely get Anvil onto a spammers blocklist.

I see. You want to test the password in isolation, independent of email address, for whether Anvil would accept it.

Looking at Configuration Options in

I did not find a clear way to replicate how Anvil does its more advanced checks.

But others may have some ideas.

I’d be tempted to have a poke around in the runtime source code

1 Like

…found an is-password-pwned function in the core utilities clojure files, I don’t write lisp, but I can spot an API endpoint request when I see one:

(defn is-password-pwned? [password]
  (let [sha1sum ^String (digest/sha-1 password)

        r (:body @(http/request {:keepalive -1
                                 :url       (str "https://api.pwnedpasswords.com/range/" (.substring sha1sum 0 5))
                                 :headers   {"user-agent" "Anvil"}}))
        lines (when r (.split r "(?m)\n"))]

    (some #(-> (.toLowerCase ^String %)
               (.split ":")
               (first)
               (= (.substring sha1sum 5)))
          lines)))
1 Like

…and here is the link to the reference for how to use that particular API endpoint with HTTP requests:

To be clear this is the function used, I didn’t just find something that looks like its named correctly.

Following from here in the users service it goes to this :point_up: server call → native-rpc / core.clj → a function named signup-with-email → a call to is-password-pwned from runtime-util / util.clj → contains the HTTP endpoint code referenced in the above post.

Made a clone link example, it seems to work:

https://anvil.works/build#clone:WWHZUEM7BXMPC5NL=2ZJHXPWQDOGXLRSCE4CYB5IQ

import anvil.http
from hashlib import sha1

def is_pswd_pwnd(pswd="password"):
  pswd = sha1(pswd.encode()).hexdigest().upper()

  try:
    response = anvil.http.request(url=f"https://api.pwnedpasswords.com/range/{pswd[:5]}",
                    method="GET",
                    headers= {
                      "user-agent": "Anvil",
                    })
  except anvil.http.HttpError as err:
    print(f"Error {err.status}")
    #print(str(err.content))
    
  response = response.get_bytes().decode('utf-8')

  return pswd[5:] in response