Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ export interface BaseOrganizationMembership {
}

export interface OrganizationMembership extends BaseOrganizationMembership {
/** The name of the organization which the user belongs to. */
organizationName: string;
/** The organization's name, absent from event and action payloads. */
organizationName?: string;
/** The primary role assigned to the user within the organization. */
role: RoleResponse;
roles?: RoleResponse[];
Expand All @@ -34,7 +34,7 @@ export interface BaseOrganizationMembershipResponse {
object: 'organization_membership';
id: string;
organization_id: string;
organization_name: string;
organization_name?: string;
status: OrganizationMembershipStatus;
user_id: string;
directory_managed?: boolean;
Expand Down
64 changes: 64 additions & 0 deletions src/webhooks/webhooks.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ import mockWebhook from './fixtures/webhook.json';
import { SignatureVerificationException } from '../common/exceptions';

// @oagen-ignore-start
import type {
OrganizationMembership,
OrganizationMembershipResponse,
} from '../user-management/interfaces';

describe('Webhook signatures', () => {
let payload: any;
let secret: string;
Expand Down Expand Up @@ -84,6 +89,65 @@ describe('Webhook signatures', () => {
});

describe('constructEvent', () => {
describe.each([
'organization_membership.created',
'organization_membership.updated',
'organization_membership.deleted',
])('%s', (event) => {
it.each([undefined, 'Example Organization'])(
'accepts an organization name of %s',
async (organizationName) => {
const membership: OrganizationMembershipResponse = {
object: 'organization_membership',
id: 'om_123',
organization_id: 'org_123',
user_id: 'user_123',
status: 'active',
role: { slug: 'member' },
created_at: '2023-11-16T21:32:25.235Z',
updated_at: '2023-11-16T21:32:25.235Z',
...(organizationName && { organization_name: organizationName }),
};
const expected: OrganizationMembership = {
object: 'organization_membership',
id: 'om_123',
organizationId: 'org_123',
userId: 'user_123',
status: 'active',
role: { slug: 'member' },
directoryManaged: false,
customAttributes: {},
createdAt: new Date(membership.created_at),
updatedAt: new Date(membership.updated_at),
...(organizationName && { organizationName }),
};
const rawBody = JSON.stringify({
id: 'event_123',
event,
data: membership,
created_at: membership.created_at,
});
const hash = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`)
.digest('hex');

const webhook = await workos.webhooks.constructEvent({
payload: rawBody,
sigHeader: `t=${timestamp}, v1=${hash}`,
secret,
});

expect(webhook.event).toBe(event);
expect(webhook.data).toEqual(expected);
expect(webhook.data).toHaveProperty(
'organizationName',
organizationName,
);
},
);
});

describe('with the correct payload, sig_header, and secret', () => {
it('returns a webhook event', async () => {
const sigHeader = `t=${timestamp}, v1=${signatureHash}`;
Expand Down
Loading