auth0/terraform-provider-auth0

Support a User resource to attach a TOTP SECRET to a user

Opened this issue · 0 comments

Checklist

Describe the problem you'd like to have solved

We use Auth0 in many of our environments. As part of our CICD pipeline we create a new test user (database connection) and link it to the organisation and assign it roles all via the auth0 provider. The test user is then used by other jobs in the pipeline to run cypress tests against our apps web UI. However our settings mandate that users in the database connections must have MFA.

Previously our QA team would have to log in to Hashicorp Vault where we store the test username and password, then log in to the app where they would be prompted to follow the MFA journey. They would copy the OTP SECRET and use it to generate OTP codes. That OTP secret would then also need to be stored manually in Vault.

Then we run the test stage of the pipeline to run the cypress tests. That job looks up the username, password and OTP SECRET from vault and uses it to successfully login. However this is very manual and means we need the pipeline to pause while a QA does the above actions.

We recently automated the OTP SECRET via a docker container and the go-auth0 SDK which allows us to generate an OTP SECRET and attach it to the user. This works well and we are generating and storing the OTP SECRET with terraform and into vault.

Given that this functionality is already in the go-auth SDK it would be nice if we could just also do this TOTP for the user via a terraform resource. Instead of having to run a separate pipeline job in a go container to do it directly via the go-auth SDK

Describe the ideal solution

A terraform resource for that could attach a TOTP authentication method to a user. An example schema could be

// NewAuthenticationMethodResource will return a new auth0_connection_client resource.
func NewAuthenticationMethodResource() *schema.Resource {
	return &schema.Resource{
		Schema: map[string]*schema.Schema{
			"user_id": {
				Type:        schema.TypeString,
				Required:    true,
				ForceNew:    true,
				Description: "ID of the user to associate the permission to.",
			},
			"type": {
				Type:        schema.TypeString,
				Required:    true,
				ForceNew:    true,
				Description: "The type of the authentication method.",
			},
			"name": {
				Type:        schema.TypeString,
				Required:    true,
				ForceNew:    true,
				Description: "A human-readable label to identify the authentication method.",
			},
			"totp_secret": {
				Type:        schema.TypeString,
				Optional:    true,
				ForceNew:    true,
				Sensitive:   true,
				Description: "Base32 encoded secret for TOTP generation.",
			},
		},
		CreateContext: createUserAuthMethodTOTP,
		ReadContext:   readUserAuthMethodTOTP,
		DeleteContext: deleteUserAuthMethodTOTP,
		Importer: &schema.ResourceImporter{
			StateContext: internalSchema.ImportResourceGroupID("user_id", "type", "name", "totp_secret"),
		},
		Description: "With this resource, you can manage user authentication methods.",
	}
}
Terraform will perform the following actions:

  # auth0_user.user will be created
  + resource "auth0_user" "user" {
      + connection_name = "local-dev-test-org-internal-users"
      + email           = "test@test.com"
      + email_verified  = true
      + id              = (known after apply)
      + name            = (known after apply)
      + nickname        = (known after apply)
      + password        = (sensitive value)
      + picture         = (known after apply)
      + user_id         = (known after apply)
    }

  # auth0_user_authentication_method.totp will be created
  + resource "auth0_user_authentication_method" "totp" {
      + id          = (known after apply)
      + name        = "My TOTP2"
      + totp_secret = (sensitive value)
      + type        = "totp"
      + user_id     = (known after apply)
    }

Plan: 2 to add, 0 to change, 0 to destroy.
auth0_user.user: Creating...
auth0_user.user: Creation complete after 1s [id=auth0|66e58059780b663974a12bc2]
auth0_user_authentication_method.totp: Creating...
auth0_user_authentication_method.totp: Creation complete after 0s [id=totp|dev_I5Rk8f1inKvcYCYe]

Additionally instead of the totp_secret having to be passed to the resource, if not provided the resource could generate a random one and expose it to the caller to allow it to be stored else where like Hashicorp Vault.

Alternatives and current workarounds

As a work around we have written a docker container that implements logic to add a TOTP to the user. this is not ideal as it needs to run in a job after our terraform and relies on extracting the new user out of the terraform outputs and then running this go container as a seprate job

func createTotp(api *management.Management, user *management.User, totpName, totpSecret string) {
	userEmail := user.GetEmail()
	totpAuthMethod := &management.AuthenticationMethod{
		Type:       auth0.String("totp"),
		Name:       auth0.String(totpName),
		TOTPSecret: auth0.String(totpSecret),
	}

	// For this to work the management token needs to have the CRUD permissions for `create:authentication_methods`
	// scope in the management api
	if userHasNamedTotp(api, user, totpName) {
		log.Printf("Totp already exists for user %s\n", userEmail)
		return
	}

	err := api.User.CreateAuthenticationMethod(context.Background(), user.GetID(), totpAuthMethod)
	if err != nil {
		log.Fatalf("failed to create totp for user: %v", err)
	}
	log.Printf("Created totp for user %s\n", userEmail)
}

My suggestion to create an authMethodTOTP could be extended out to support all auth methods in the AuthenticationManagement struct but this would significantly complicate the resource and testing as the Struct supports several auth method types so i think its cleaner to implement these as separate resources.

Additional context

I have made a base implementation for this which doesnt have tests yet but I have verified manually that it works. I am happy to build out the docs and tests for this if people think it has value and would be accepted

main...cdsre:terraform-provider-auth0:user-authentication-methods