Email verification for Enterprise users

BTW; you cannot map literals

"email_verified": true

for some reason.

Any updates about this? users created with enterprise connection should be able to set as “email_verified”: true. right now it’s not possible like any other mapping! also as mentioned before, mapping “email_verified” attribute to literal true value is not setting the created users to verified after being created!

1 Like

Bump. Any updates on this? Being able to set the value of the email verified flag by default for SAML connections is a must.

Same for us as well. Plus, mapping to a “truthy” value breaks the Golang client that expects an actual boolean.

2 Likes

Ditto. This just broke one of our customers. The Auth0 Golang SDK requires “email_verified” to be a boolean, and will fail when it’s a string.

Bump. Not being able to set “email_verified” to true form SAML connections is breaking all of the Auth0 SDKs for reading users. We currently have to map to “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier” but that is not a sufficient solution given the issue above.

In case it can help anybody else, the workaround we ended up with is to wrap the resulting user list so we can use a custom unmarshal function.

// Specialized SAML user list function
//
// The Auth0 golang SDK does not properly handle the unmarshal of the returned payload into a management.UserList.
//
// The returned payload contains:
// "email":"user@domain.com",
// "emailVerified":"true",
// "email_verified":"user@domain.com"
//
// Which results in an unmarshal error when calling `func (m *UserManager) List(ctx context.Context, opts ...RequestOption) (ul *UserList, err error)`
// resulting in an error `strconv.ParseBool: parsing "user@domain.com": invalid syntax`
//
// The implementation below works around the issues by using custom JSON marshaling to map the values into the management.User instances.
type User struct {
	management.User
}

type UserList struct {
	management.List
	Users []*User `json:"users"`
}

func (ul UserList) UserList() []*management.User {
	return lo.Map(ul.Users, func(v *User, i int) *management.User { return &v.User })
}

func (u *User) UnmarshalJSON(b []byte) error {
	var raw map[string]interface{}

	if err := json.Unmarshal(b, &raw); err != nil {
		return err
	}

	verified := false
	if emailVerified, ok := raw["emailVerified"]; ok {
		verified, _ = strconv.ParseBool(emailVerified.(string))
	}

	delete(raw, "emailVerified")
	delete(raw, "email_verified")

	buf, err := json.Marshal(raw)
	if err != nil {
		return err
	}

	type tTmpUser User
	var tmpUser tTmpUser

	if err := json.Unmarshal(buf, &tmpUser); err != nil {
		return err
	}
	tmpUser.VerifyEmail = &verified

	*u = User(tmpUser)

	return nil
}

func ListUsers(ctx context.Context, m *management.Management, payload interface{}, options ...management.RequestOption) error {
	options = append(options,
		management.PerPage(100),
		management.IncludeTotals(true),
	)

	request, err := m.NewRequest(ctx, "GET", m.URI("users"), payload, options...)
	if err != nil {
		return fmt.Errorf("failed to create a new request: %w", err)
	}

	response, err := m.Do(request)
	if err != nil {
		return fmt.Errorf("failed to send the request: %w", err)
	}
	defer response.Body.Close()

	// If the response contains a client or a server error then return the error.
	if response.StatusCode >= http.StatusBadRequest {
		return err // newError(response) //TODO create correct error based on response
	}

	responseBody, err := io.ReadAll(response.Body)
	if err != nil {
		return fmt.Errorf("failed to read the response body: %w", err)
	}

	if len(responseBody) > 0 && string(responseBody) != "{}" {
		if err = json.Unmarshal(responseBody, &payload); err != nil {
			return fmt.Errorf("failed to unmarshal response payload: %w", err)
		}
	}

	return nil
}

Link to the code which deploys this for further detail: