Best way to connect an API call to a user without authentication, instead using metadata field?

I am building an API that needs to allow users to make calls to it from their website to store tracking data. This means their web client will not be authenticated, but the API needs some way to connect the call to a user.

This kind of thing is inherently insecure, so I just need a simple way to connect API calls to a user. I’ll handle abuse or false data later.

My initial idea was to add a secret key to the user’s app_metadata, in hopes that I could do some sort of query on users by app_metadata. I’d like to do something like this (just using sql as pseudo code here)

SELECT from users
where secret = $1;

Then if a user shows up, use their id as ownership for tracking data. If not, return an error to the web client… But then realized I don’t think I have a way to query users by metadata in auth0. I think I saw a way to do some kind of API calls on user signup but this sounds like it would be horrible to test locally.

I could use a “custom database” and just plug into the Postgres db I’m already using for my other data, but that seems like it isn’t recommended and might cause problems down the line.

So I’m just wondering if anyone else has run into this and if there is a recommended way to handle this.

Thank you :slight_smile:

Hi @zacharyholland,

I’m not sure if this answers your question, but if you give your API read:users (or users:read. going on memory here) access to the Auth0 management API, you can certainly perform a query like "get me the user where app_metadata.secret = $1".

Interesting, would it be using this API?

The Management API documentation is here:

https://auth0.com/docs/api/management/v2

Every Auth0 tenant has a Management API and an Authentication API that you can use. The users endpoint documentation is here:

https://auth0.com/docs/api/management/v2#!/Users/get_users

You can experiment with the APIs using postman. Postman collections for both APIs can be loaded here:

Yep and let us know if you have any further questions!

Cool thank you! I’ll try implementing this tonight and give an update after.

Sure thanks a lot for that!

Hello! I ended up taking Friday off to do some fishing so got a bit delayed.
My first step was to create a new Machine to Machine application for my backend and enable client grants for it. I mainly followed this short tutorial to do that.

I decided to use the go-auth0/auth0 go package in my backend API to make management calls (like querying users). The documentation is really confusing on that package, and took a while to find the correct examples.

In the end, my implementation is pretty simple. I first initialize the management object in my main function and inject it into my Users DAO service:

m, err := management.New(auth0Domain, auth0ClientID, auth0Secret, management.WithDebug(true))
if err != nil {
	panic(err)
}

usersDAO := &auth0.UsersDAO{
	Manager: m,
}

It’s then used like this in the DAO package:

func (dao *UsersDAO) FindBySecret(secret string) (*app.User, error) {
	queryStr := fmt.Sprintf(`app_metadata.secret:"%s"`, secret)
	query := management.Query(queryStr)
	users, err := dao.Manager.User.List(query)
	if err != nil {
		return nil, err
	}
	// ... continued later ...

This gives me a list of users which is perfect! I then do some checks on the data and convert it to my internal User struct. This is just to keep the internal language the same across all the services and data access objects:

	// .. continued ...
	if users.Length > 1 {
		errStr := "Found multiple users for one secret key"
		// Note: This error is serious af... idk how this could happen
		log.Println(errStr)
		return nil, errors.New(errStr)
	}

	user := users.Users[0]

	return &app.User{
		Name:  *user.Name,
		Email: *user.Email,
		UUID:  user.AppMetadata["uuid"].(string),
	}, nil
}

This might be a bit too much information, but I thought it would be nice to share my entire implementation since I don’t see much info about using the management API in Go.

It would actually be really nice if this package gave access the raw JSON interface so I could unmarshal it into my own User struct. I’ve been thinking about just marshalling it back to json, then unmarshalling it into my struct but that seems like a bit much, especially because I’d still have to do something like this to grab the uuid from metadata… I’ll stop brain dumping now haha

For now, this works just fine :slight_smile:

1 Like

This topic was automatically closed 15 days after the last reply. New replies are no longer allowed.