K
Kinde4mo ago
Sophia

Creating users via Kinde API

Hi there! Super new to Kinde and web development in general so apologies for my lack of knowledge in advance. I'm trying to do something that I hope can be done via Kinde but I'm not 100% sure - I'd like for a logged in user with a specific role (ie a manager) to be able to add users to Kinde (within their organisation) via our site using the Kinde API. Our app is full-stack NextJS using app routing with TypeScript, so I looked through the NextJS SDK documentation. I first got very stuck looking at the code snippets there as I couldn't find any documentation for how to use the API with createKindeManagementAPIClient especially trying to use the createUser method on the usersApi. I then tried to just use fetch() using the code samples on the API docs themselves, but had issues with the access token. I've tried to obtain the access token via Postman which worked fine, so I think my setup on Kinde is correct, but for some reason the client authentication fails when I try to run it locally in my code. Could this be because I'm using http://localhost:3000 for testing? I've tried setting the "audience" parameter to this URL to see if this resolves it. I'll follow up with a comment containing the code I've currently got in my component where I'm trying to add users. The env variables are the ones for my M2M application in Kinde. I've switched the API on for this application. It's currently not getting to the addUser() function because the accessToken is coming back undefined because the client authentication isn't working. Any ideas as to why this could be? Is it the domain or have I missed something else? Thank you in advance ❤️
7 Replies
Sophia
Sophia4mo ago
async function getAccessToken() { const url = 'https://kettleon.kinde.com/oauth2/token'; const clientId = process.env.KINDE_CLIENT_M2M_ID const clientSecret = process.env.KINDE_CLIENT_M2M_SECRET const options = { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'client_credentials', client_id: ${clientId}, client_secret: ${clientSecret}, audience: 'http://localhost:3000' }) }; const response = await fetch(url, options); const data = await response.json(); console.log(data) console.log("token", data.access_token) return data.access_token; } const addUser = async (e : any) => { e.preventDefault(); const accessToken = await getAccessToken() const inputBody = { "profile": { "given_name": ${e.target.given_name.value}, "family_name": ${e.target.family_name.value}, }, "organization_code": ${props.organisation}, "identities": [ { "type": "email", "details": { "email": ${e.target.email.value} } } ] }; const headers = { 'Content-Type':'application/json', 'Accept':'application/json', 'Authorization':Bearer ${accessToken}, 'Access-Control-Allow-Origin':'https://kettleon.kinde.com' }; await fetch('https://kettleon.kinde.com/api/v1/user', { method: 'POST', body: inputBody, headers: headers }) .then(function(res) { return res.json(); }).then(function(body) { console.log(body); }); }
Oli - Kinde
Oli - Kinde4mo ago
Hey @Sophia, Thanks for reaching out, I can definitely help you with this. It looks like you're on the right track with your code for adding users via the Kinde API. However, there are a couple of adjustments and clarifications that might help resolve the issues you're encountering. 1. Audience Parameter: The audience parameter in your getAccessToken function is currently set to 'http://localhost:3000'. This is not correct. The audience parameter should be set to the identifier for the Kinde Management API, not your local development URL. Based on the provided code and context, it should likely be 'https://kettleon.kinde.com/api'. This is crucial for obtaining a valid access token that can be used to authenticate your API requests. 2. Access-Control-Allow-Origin Header: The 'Access-Control-Allow-Origin':'https://kettleon.kinde.com' header in your addUser function is not necessary and might be incorrect in this context. This header is used by servers to indicate which origins are allowed to read the response in a cross-origin request, and it's not something you set on the request from the client side. The server (in this case, Kinde) dictates this policy. You can remove this line from your headers object. 3. String Interpolation in JSON: When constructing your inputBody, you're using template literals incorrectly inside a JSON string. This will not automatically convert the values to strings or escape them as needed. Instead, consider constructing your JSON object as an actual JavaScript object and then stringify it when making the fetch call. This approach also makes it easier to handle dynamic values correctly. Here's a revised version of your addUser function with these adjustments:
const addUser = async (e) => {
e.preventDefault();

const accessToken = await getAccessToken()

const inputBody = {
profile: {
given_name: e.target.given_name.value,
family_name: e.target.family_name.value,
},
organization_code: props.organisation,
identities: [
{
type: "email",
details: {
email: e.target.email.value
}
}
]
};

const headers = {
'Content-Type':'application/json',
'Accept':'application/json',
'Authorization':`Bearer ${accessToken}`
};

await fetch('https://kettleon.kinde.com/api/v1/user',
{
method: 'POST',
body: JSON.stringify(inputBody),
headers: headers
})
.then(function(res) {
return res.json();
}).then(function(body) {
console.log(body);
});
}
const addUser = async (e) => {
e.preventDefault();

const accessToken = await getAccessToken()

const inputBody = {
profile: {
given_name: e.target.given_name.value,
family_name: e.target.family_name.value,
},
organization_code: props.organisation,
identities: [
{
type: "email",
details: {
email: e.target.email.value
}
}
]
};

const headers = {
'Content-Type':'application/json',
'Accept':'application/json',
'Authorization':`Bearer ${accessToken}`
};

await fetch('https://kettleon.kinde.com/api/v1/user',
{
method: 'POST',
body: JSON.stringify(inputBody),
headers: headers
})
.then(function(res) {
return res.json();
}).then(function(body) {
console.log(body);
});
}
Make sure your environment variables (KINDE_CLIENT_M2M_ID and KINDE_CLIENT_M2M_SECRET) are correctly set to the values for your machine-to-machine application in Kinde. This is essential for the getAccessToken function to work properly. If you continue to face issues, please check the exact error message you're receiving, as it can provide more insights into what might be going wrong. Let me know if you have any further questions.
Our app is full-stack NextJS using app routing with TypeScript, so I looked through the NextJS SDK documentation. I first got very stuck looking at the code snippets there as I couldn't find any documentation for how to use the API with createKindeManagementAPIClient especially trying to use the createUser method on the usersApi.
I will pass on this feedback, there is definitely an opportunity to make our NextJS docs better.
Sophia
Sophia4mo ago
Thank you for getting back to me! I tried the steps above and still received an error, but have realised the issue I was having with obtaining the access token was entirely my own fault and as a result of my misunderstanding how to make env variables accessible in NextJS. My bad! I am now receiving the access token, but I'm still receiving a CORS error. Funnily enough this is why I attempted to use Access-Control-Allow-Origin, though I understand I was using it incorrectly! The attached screenshot shows the CORS error I'm receiving. I'm wondering if this is to do with the host I'm working on? I've amended the addUser function such that it looks like this: const addUser = async (e : any) => { e.preventDefault(); const accessToken = await getAccessToken() const inputBody = { "profile": { "given_name": e.target.given_name.value, "family_name": e.target.family_name.value, }, "organization_code": props.organisation, "identities": [ { "type": "email", "details": { "email": e.target.email.value } } ] }; const headers = { 'Content-Type':'application/json', 'Accept':'application/json', 'Authorization':Bearer ${accessToken}, }; await fetch('https://kettleon.kinde.com/api/v1/user', { method: 'POST', body: JSON.stringify(inputBody), headers: headers }) .then(function(res) { return res.json(); }).then(function(body) { console.log(body); }); } Is there anything I'm missing here?
No description
Sophia
Sophia4mo ago
I tried asking Kinde AI too but not to much avail: https://discord.com/channels/1070212618549219328/1215295730151854140
Oli - Kinde
Oli - Kinde4mo ago
Hey @Sophia, I will get one of my teammates to help you out.
Sophia
Sophia4mo ago
That's great, thank you!
Oli - Kinde
Oli - Kinde4mo ago
Hey @Sophia, The problem here is that you are attempting to make a call to the Kinde Management API from a browser session. You can only call our Kinde Management API from backend service. Do you have a backend/server in your setup?