How do I allow users to view/update their user_metadata?

I am using React with auth0-spa-js, and I am having issues trying to get/set user metadata.
I’ve followed the sample react code to create a basic application which works great. I am trying to add a page which allows users to update their profile data (change picture, meta data and stuff), but if I try to get or set the user profile, it comes back with a 400 error:

{“statusCode”:400,“error”:“Bad Request”,“message”:“Bad HTTP authentication header format”,“errorCode”:“Bearer”}

Any ideas? Here’s the sample code:
import React from ‘react’;
import axios from ‘axios’;
import { useAuth0 } from “…/…/components/Auth0Wrapper”;

const apiBase = ‘https://[my api].auth0.com’;
const getUserApiPath = (user) => ${apiBase}/api/v2/users/${user.sub};

export default () => {

const { getTokenSilently, user } = useAuth0();

const updateUserProfile = (userProfile) => {
	return getTokenSilently().then(accessToken => {
			return axios.patch(getUserApiPath(user), {
					...userProfile
				},
				{
					headers: {
						Authorization: `Bearer ${accessToken}`,
						'Content-Type': 'application/json'
					}
				}
			)
		}).then(
		success => {
			console.log('changed successfully', success);
		},
		fail => {
			console.log('failed', fail);
		});
}

const getUserProfile = () => {
	return getTokenSilently().then(accessToken => {
		return axios.get(getUserApiPath(user), {
			headers: {
				Authorization: `Bearer ${accessToken}`
			}
		});
		}).then(
		success => {
			console.log('retrieved successfully', success);
		},
		fail => {
			console.log('failed', fail);
		});
}

updateUserProfile({
	user_metadata: { 'some_property': 'test value' }
}).then(() => getUserProfile());

return (
	<div>
		<h1>Profile Settings</h1>
		<pre>
			{user.user_metadata}
		</pre>
	</div>
);

}

Hi @truescope,

I think this is because you haven’t declared the management api as an audience for the token. The managment API is seperate from the authentication API.

Take a look at this doc:

If you toggle to the node implementation you can probably just use that, if you use request instead of axios. Or just tailor it to axios, whatever you are comfortable with. Let me know if that does it.

Thanks,
Dan

P.S. as a reminder, SPAs are limited in the scopes they can request from the management api

Thanks dan. As you’ve pointed out, my requests weren’t authorized to access the management api.
I fixed this by requesting a token silently with the scope as the audience. This sample demonstrates:

import React, { useState, useEffect } from 'react';
import axios from 'axios';
import { useAuth0 } from "./Auth0Wrapper";
import { stringIsNullOrEmpty } from '../utils/strings.js';

/**
 * definitions for each api that i want to use
*/
const apiConfig = {
    custom:{
        baseURL: "https:// [actual api path] .com",
        audience: "https:// [custom api audience]",
        scope: null
    },
    management:{
        baseURL: "https:// [custom domain or {tenant}.{region}.auth0.com] .com/api/v2/",
        audience: "https:// [management api audience]",
        scope: "openid profile email read:current_user update:current_user_metadata"
    }
}

/**
 * creates an API and applies to token
 */
const createApi = (baseURL, token) => axios.create({
    baseURL,
    headers: {
        Authorization: `Bearer ${token}`,
        'Content-Type': 'application/json'
    }
});

/**
 * gets a token silently. If this fails, get it with a popup. then use it to create the api
 */
const createLazyTokenizedApi = (getTokenSilently, getTokenWithPopup, apiConfig) => {

    let options = {
        audience: apiConfig.audience
    };

    if(!stringIsNullOrEmpty(apiConfig.scope))
        options.scope = apiConfig.scope;

    return () => getTokenSilently(options).then(accessToken => 
        {
            console.log('silently retrieved token', accessToken);
            return createApi(apiConfig.baseURL, accessToken);
        },
        fail => {
            console.log('failed to get token', fail);
            return getTokenWithPopup(options).then(
                accessToken => {
                    console.log('popup retrieved token', accessToken);
                    return createApi(apiConfig.baseURL, accessToken);
                },
                fail => {
                    console.error('cannot get token', fail);
                });
        }
    );
}

/**
 * creates all apis that I want to use in the application
 */
const createApiLookup = (getTokenSilently, getTokenWithPopup) => {
	return {
		getManagementApi: createLazyTokenizedApi(getTokenSilently, getTokenWithPopup, apiConfig.management),
		getCustomApi: createLazyTokenizedApi(getTokenSilently, getTokenWithPopup, apiConfig.custom),
	};
}

/**
 * main application
 */
export default () => {

    const { getTokenSilently, getTokenWithPopup, user } = useAuth0();

    const apiLookup = createApiLookup(getTokenSilently, getTokenWithPopup);

	let [userProfile, setUserProfile] = useState({});

    useEffect(() => { 
        apiLookup.getManagementApi().then(api => {
            const path = `/users/${user.sub}`;
            return api.get(path).then(
                ({ data }) => setUserProfile(data),
                fail => console.log('failed', fail)
            )
        });
    }, []);

    return (
        <div>
            <h1>My Auth0 React Application</h1>
            <h3>user profile:</h3>
            <pre>{JSON.stringify(userProfile, null, 2)}</pre>
        </div>
    );
}
1 Like

Thanks for posting your solution! Glad we figured it out.

Best,
Dan

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