API Two-Factor Authentication

Our studio will be moving to TFA on Shotgun and I’m still unclear on how that would affect our scripts. The documentation I’ve found doesn’t seem to cover this but maybe I’m just looking in the wrong places.

We have 3 different ways of accessing the Shotgun API:

  • Via local python scripts on on-prem workstations - These use script keys
  • Via AWS lambda webhooks (also python API) - These use script keys
  • Via HTML/Javascript pages that we host - These use the REST API and a user has to login to use it. (Mostly for graphing, status reports, and searches that can’t be done in the shotgun web interface)

I’m primarily concerned with the last one but I also want to be sure our script keys won’t break when we make the move.

In searching I found this post but it still isn’t clear to me how I get that auth_token for TFA.
I would love an example of the full flow for how to make a page that asks a user to login, does the TFA, and then gives them an access_token and refresh_token like we currently use.

Any examples or resources would be appreciated.


Hi @Jase_Lindgren,

Please check Two Factor Auth error using REST API for a thread involving the same kinds of requirements.

Hope it helps.

1 Like

Hi @Jase_Lindgren,

My answer assumes that you are using Shotgun’s own two-factor authentication support, and that you are not using SSO for Shotgun Authentication.

In short, 2FA will only impacts connections initiated using a username/password pairs. It will not impact connections initiated with ScriptName/APIKey. From what you describe of your environment, your existing Python API-using scripts will continue to work as before.

For connections initiated with a username/password, that user will need to provide a code from the Google Authenticator app or Duo Mobile App using their smartphones. The same way they’ll need to do when authenticating into Shotgun once 2FA is turned on.

That code is what needs to be passed on as the auth_token for your call to get the OAuth2 access token. I believe that you will need to modify the authentication page of your web application to also ask for the user’s 2FA code.

I would suggest that you do your auth in 2 steps, the way shotgun does it. First you ask for the username and password. If you get HTTP status of 400, and an error code 106 it means a 2FA token is expected, so then you can show a second page that ask the user for that code. Once you have the user’s code, you modify your API call to Shotgun to include it. This way you can publish your changes ahead of your Shotgun server switching to 2FA.

Example of an API call that fails due to a missing 2FA code:

curl -X POST https://yoursite.shotgunstudio.com/api/v1/auth/access_token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'Accept: application/json' \
-d 'username=a_username&password=a_password&grant_type=password'

    "errors": [
            "code": 106,
            "detail": null,
            "id": "243dd3f5f1875cecc88d52e0eff8c72c",
            "meta": null,
            "source": {
                "auth_token": "missing for 2FA login"
            "status": 400,
            "title": "Missing argument for 2FA login"

(I took the liberty of pretty-printing the resulting json payload)

You would then modify your call, adding the 6-number 2FA code:

curl -X POST https://yoursite.shotgunstudio.com/api/v1/auth/access_token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'Accept: application/json' \
-d 'username=a_username&password=a_password&auth_token=123456&grant_type=password'

At this point, assuming the credentials are correct, you would get the expected payload with the Bearer token.

    "token_type": "Bearer",
    "access_token": "an_access_token",
    "expires_in": 3600,
    "refresh_token": "a_refresh_token"

For others looking at this thread, the relevant documentation pages are:

Hoping you’ll find my answer useful,



Yes, that’s the post I linked to in my original question.

1 Like

Thank you, Patrick!

That is exactly the info I was looking for. That piece about the specific error code is exactly what I needed.


My mistake @Jase_Lindgren , glad you got a solution :slight_smile: