K
Kinde•4mo ago
Kenton

Kinde Management API - 400 Bad Request: malformed Host header

I am trying to create an Organization and move a user that's in the Default Organization into it using the Kinde Management API; however, I am getting the error:
Error creating organization or adding user: A [ApiError]: Error creating user.
at xt (webpack-internal:///(rsc)/./node_modules/@kinde/management-api-js/dist/kinde-management-api-js.cjs:1:25453)
at eval (webpack-internal:///(rsc)/./node_modules/@kinde/management-api-js/dist/kinde-management-api-js.cjs:1:26058)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
url: 'https://{your_subdomain}.kinde.com/api/v1/organization',
status: 400,
statusText: 'Bad Request: malformed Host header',
body: '400 Bad Request: malformed Host header',
request: {
method: 'POST',
url: '/api/v1/organization',
body: { name: 'test-org', handle: 'test-org' },
mediaType: 'application/json',
errors: {
'400': 'Error creating user.',
'403': 'Invalid credentials.',
'429': 'Request was throttled.',
'500': 'Could not create organization.'
}
}
}
Error creating organization or adding user: A [ApiError]: Error creating user.
at xt (webpack-internal:///(rsc)/./node_modules/@kinde/management-api-js/dist/kinde-management-api-js.cjs:1:25453)
at eval (webpack-internal:///(rsc)/./node_modules/@kinde/management-api-js/dist/kinde-management-api-js.cjs:1:26058)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
url: 'https://{your_subdomain}.kinde.com/api/v1/organization',
status: 400,
statusText: 'Bad Request: malformed Host header',
body: '400 Bad Request: malformed Host header',
request: {
method: 'POST',
url: '/api/v1/organization',
body: { name: 'test-org', handle: 'test-org' },
mediaType: 'application/json',
errors: {
'400': 'Error creating user.',
'403': 'Invalid credentials.',
'429': 'Request was throttled.',
'500': 'Could not create organization.'
}
}
}
I started by trying to enable Kinde Management API in the Kinde Console for my NextJS app but got the error: "m2m applications can have access the kinde management api" So I create a M2M application, added the following env variables:
KINDE_DOMAIN=https://my-handle.kinde.com
KINDE_MANAGEMENT_CLIENT_ID=m2m-application-client-id
KINDE_MANAGEMENT_CLIENT_SECRET=m2m-application-client-secret
KINDE_DOMAIN=https://my-handle.kinde.com
KINDE_MANAGEMENT_CLIENT_ID=m2m-application-client-id
KINDE_MANAGEMENT_CLIENT_SECRET=m2m-application-client-secret
7 Replies
Kenton
KentonOP•4mo ago
I created a POST request in my NextJS app to create the org and move the user:
import {NextRequest, NextResponse} from "next/server";
import {Organizations} from "@kinde/management-api-js";

export async function POST(req: NextRequest) {
try {
const orgResponse = await Organizations.createOrganization({
requestBody: {
name: "test-org",
handle: "test-org",
},
});

const orgCode = orgResponse.organization?.code;
const userId = "hard_coded_user_id"
if (orgCode && userId) {
const response = await Organizations.addOrganizationUsers({
orgCode: orgCode,
requestBody: {
users: [{ id: userId }],
},
});
console.log("User added successfully:", response);
} else {
console.error("Invalid orgCode or user ID");
}
} catch (error) {
console.error("Error creating organization or adding user:", error);
}

return NextResponse.json({});
}
import {NextRequest, NextResponse} from "next/server";
import {Organizations} from "@kinde/management-api-js";

export async function POST(req: NextRequest) {
try {
const orgResponse = await Organizations.createOrganization({
requestBody: {
name: "test-org",
handle: "test-org",
},
});

const orgCode = orgResponse.organization?.code;
const userId = "hard_coded_user_id"
if (orgCode && userId) {
const response = await Organizations.addOrganizationUsers({
orgCode: orgCode,
requestBody: {
users: [{ id: userId }],
},
});
console.log("User added successfully:", response);
} else {
console.error("Invalid orgCode or user ID");
}
} catch (error) {
console.error("Error creating organization or adding user:", error);
}

return NextResponse.json({});
}
Can anyone see what I'm doing wrong?
Daniel_Kinde
Daniel_Kinde•4mo ago
Thanks for this @Kenton , let me check this out for you. Hi @Kenton , I didn't spot immediately, the route is missing the init method, this is required to set the management SDK correctly. The aim is to have this auto configure however some frameworks his was problematic, hope to address in later releases. Below is updated code, tested and working.
import {NextRequest, NextResponse} from "next/server";
import {Organizations, init} from "@kinde/management-api-js";

export async function POST(req: NextRequest) {
try {
init();

const orgResponse = await Organizations.createOrganization({
requestBody: {
name: "test-org",
handle: "test-org",
},
});

const orgCode = orgResponse.organization?.code;
const userId = "hard_coded_user_id"
if (orgCode && userId) {
const response = await Organizations.addOrganizationUsers({
orgCode: orgCode,
requestBody: {
users: [{ id: userId }],
},
});
console.log("User added successfully:", response);
} else {
console.error("Invalid orgCode or user ID");
}
} catch (error) {
console.error("Error creating organization or adding user:", error);
}

return NextResponse.json({});
}
import {NextRequest, NextResponse} from "next/server";
import {Organizations, init} from "@kinde/management-api-js";

export async function POST(req: NextRequest) {
try {
init();

const orgResponse = await Organizations.createOrganization({
requestBody: {
name: "test-org",
handle: "test-org",
},
});

const orgCode = orgResponse.organization?.code;
const userId = "hard_coded_user_id"
if (orgCode && userId) {
const response = await Organizations.addOrganizationUsers({
orgCode: orgCode,
requestBody: {
users: [{ id: userId }],
},
});
console.log("User added successfully:", response);
} else {
console.error("Invalid orgCode or user ID");
}
} catch (error) {
console.error("Error creating organization or adding user:", error);
}

return NextResponse.json({});
}
Kenton
KentonOP•4mo ago
@Daniel_Kinde yesterday, I moved one of my users from the default org to a newly created org. What's surprising, is that the users orgCode in the active session didn't update to the new org the user was moved to but instead remained the same until the user session expired and the user logged back in again. I presume I'll need to do a change session of some kind after creating the new org and moving the user to that org? Hopefully the user doesn't have to login again.
Dave
Dave•4mo ago
I'm following your conversations re. is_create_org. Would this be fixed with a token refresh?
Since moving the user to the new org is part of signup i.e they wouldn't have had to use the login page yet, I'm assuming a token refresh should hopefully be painless at signup?? Just found this re. refreshing token data:- https://discord.com/channels/1070212618549219328/1276004138785833052/1276004138785833052
Peteswah
Peteswah•4mo ago
yup! I think what we want here is to call refreshTokens() from getKindeServerSession before returning the NextResponse Something like:
import {NextRequest, NextResponse} from "next/server";
import {Organizations, init} from "@kinde/management-api-js";

export async function POST(req: NextRequest) {
const {refreshTokens} = getKindeServerSession()
try {
init();

const orgResponse = await Organizations.createOrganization({
requestBody: {
name: "test-org",
handle: "test-org",
},
});

const orgCode = orgResponse.organization?.code;
const userId = "hard_coded_user_id"
if (orgCode && userId) {
const response = await Organizations.addOrganizationUsers({
orgCode: orgCode,
requestBody: {
users: [{ id: userId }],
},
});
console.log("User added successfully:", response);
} else {
console.error("Invalid orgCode or user ID");
}
} catch (error) {
console.error("Error creating organization or adding user:", error);
}
await refreshTokens();
return NextResponse.json({});
}
import {NextRequest, NextResponse} from "next/server";
import {Organizations, init} from "@kinde/management-api-js";

export async function POST(req: NextRequest) {
const {refreshTokens} = getKindeServerSession()
try {
init();

const orgResponse = await Organizations.createOrganization({
requestBody: {
name: "test-org",
handle: "test-org",
},
});

const orgCode = orgResponse.organization?.code;
const userId = "hard_coded_user_id"
if (orgCode && userId) {
const response = await Organizations.addOrganizationUsers({
orgCode: orgCode,
requestBody: {
users: [{ id: userId }],
},
});
console.log("User added successfully:", response);
} else {
console.error("Invalid orgCode or user ID");
}
} catch (error) {
console.error("Error creating organization or adding user:", error);
}
await refreshTokens();
return NextResponse.json({});
}
Although you may want to have it inside the trycatch 😄
Kenton
KentonOP•4mo ago
What's the purpose of the refreshTokens? The following works for me. create route /create-org
import {NextRequest, NextResponse} from "next/server";
import {Organizations, init} from "@kinde/management-api-js";
import {getUserAndOrg} from "@/utils/auth";

const DEFAULT_ORG_ID = 'your-default-org-id';

export async function POST(req: NextRequest) {
init();
try {
const { userId, orgId, user, error } = await getUserAndOrg();
console.log(user)
if (!userId) {
console.log("Redirect to login")
}
// Default org, prevent users from creating another org
if (orgId !== DEFAULT_ORG_ID) {
console.log("Redirect to home")
}
const orgResponse = await Organizations.createOrganization({
requestBody: {
name: "test-org",
handle: "test-org",
},
});
console.log(orgResponse)

const orgCode = orgResponse.organization?.code;
console.log(orgCode)

if (orgCode && userId) {
const response = await Organizations.addOrganizationUsers({
orgCode: orgCode,
requestBody: {
users: [{
id: userId,
roles: ["admin"]
}],
},
});

await Organizations.removeOrganizationUser({
orgCode: DEFAULT_ORG_ID,
userId: userId,
})
console.log("User added successfully:", response);
return NextResponse.json({orgId: orgCode});
} else {
console.error("Invalid orgCode or user ID");
}
} catch (error) {
console.error("Error creating organization or adding user:", error);
}

return NextResponse.json({});
}
import {NextRequest, NextResponse} from "next/server";
import {Organizations, init} from "@kinde/management-api-js";
import {getUserAndOrg} from "@/utils/auth";

const DEFAULT_ORG_ID = 'your-default-org-id';

export async function POST(req: NextRequest) {
init();
try {
const { userId, orgId, user, error } = await getUserAndOrg();
console.log(user)
if (!userId) {
console.log("Redirect to login")
}
// Default org, prevent users from creating another org
if (orgId !== DEFAULT_ORG_ID) {
console.log("Redirect to home")
}
const orgResponse = await Organizations.createOrganization({
requestBody: {
name: "test-org",
handle: "test-org",
},
});
console.log(orgResponse)

const orgCode = orgResponse.organization?.code;
console.log(orgCode)

if (orgCode && userId) {
const response = await Organizations.addOrganizationUsers({
orgCode: orgCode,
requestBody: {
users: [{
id: userId,
roles: ["admin"]
}],
},
});

await Organizations.removeOrganizationUser({
orgCode: DEFAULT_ORG_ID,
userId: userId,
})
console.log("User added successfully:", response);
return NextResponse.json({orgId: orgCode});
} else {
console.error("Invalid orgCode or user ID");
}
} catch (error) {
console.error("Error creating organization or adding user:", error);
}

return NextResponse.json({});
}
Create a /create-org page:
"use client";

import CreateOrgButton from "@/components/create-org-button";
import { useRouter } from "next/navigation";

export default function Page() {
const router = useRouter();

// Define a server action
const handleCreateOrg = async (orgName) => {
try {
const response = await fetch('/api/create-org', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: orgName }),
});

if (!response.ok) {
throw new Error('Failed to create organization');
}

const data = await response.json();
console.log('Organization created:', data);

// Should log the user in with the the new org code instead of the default org code.
router.push(`/api/auth/login?org_code=${data.orgId}`);
} catch (error) {
console.error('Error creating organization:', error);
}
};
return (
<>
<CreateOrgButton handleCreateOrg={handleCreateOrg} />
</>
)
}
"use client";

import CreateOrgButton from "@/components/create-org-button";
import { useRouter } from "next/navigation";

export default function Page() {
const router = useRouter();

// Define a server action
const handleCreateOrg = async (orgName) => {
try {
const response = await fetch('/api/create-org', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: orgName }),
});

if (!response.ok) {
throw new Error('Failed to create organization');
}

const data = await response.json();
console.log('Organization created:', data);

// Should log the user in with the the new org code instead of the default org code.
router.push(`/api/auth/login?org_code=${data.orgId}`);
} catch (error) {
console.error('Error creating organization:', error);
}
};
return (
<>
<CreateOrgButton handleCreateOrg={handleCreateOrg} />
</>
)
}
middleware.ts
import { withAuth } from "@kinde-oss/kinde-auth-nextjs/middleware";
import { NextResponse } from "next/server";

const DEFAULT_ORG_ID = 'your-default-org-id';

export default withAuth(
async function middleware(req) {
// Correctly access the pathname
const pathname = req.nextUrl.pathname;
console.log('Pathname:', pathname);

const orgCode = req.kindeAuth?.token?.org_code;
console.log('Org Code:', orgCode);

// Redirect logic based on org_code and pathname
if (pathname !== '/' && !pathname.includes('auth') && pathname !== '/create-org' && orgCode === DEFAULT_ORG_ID) {
const url = req.nextUrl.clone();
url.pathname = '/create-org';
return NextResponse.redirect(url);
}
},
{
isReturnToCurrentPage: true,
loginPage: "/login",
}
);

export const config = {
matcher: ["/home"]
};
import { withAuth } from "@kinde-oss/kinde-auth-nextjs/middleware";
import { NextResponse } from "next/server";

const DEFAULT_ORG_ID = 'your-default-org-id';

export default withAuth(
async function middleware(req) {
// Correctly access the pathname
const pathname = req.nextUrl.pathname;
console.log('Pathname:', pathname);

const orgCode = req.kindeAuth?.token?.org_code;
console.log('Org Code:', orgCode);

// Redirect logic based on org_code and pathname
if (pathname !== '/' && !pathname.includes('auth') && pathname !== '/create-org' && orgCode === DEFAULT_ORG_ID) {
const url = req.nextUrl.clone();
url.pathname = '/create-org';
return NextResponse.redirect(url);
}
},
{
isReturnToCurrentPage: true,
loginPage: "/login",
}
);

export const config = {
matcher: ["/home"]
};
This is all very hacky. Will clean it all up over the weekend so others can reuse it. Peter, still interested in knowing the purpose of the refreshTokens in this flow.
Peteswah
Peteswah•4mo ago
hey @Kenton - the purpose of refreshTokens is to sync up the the tokens stored in the cookies with Kinde data. So if Kinde data is updated via the api, data will be changed on Kinde, but it wont be reflected in the tokens (in your app) unless you refreshTokens. Hope that made sense!
Want results from more Discord servers?
Add your server