chore: reworking the API for oidc-database
Signed-off-by: benjdlambert <ben@blam.sh>
This commit is contained in:
@@ -64,7 +64,6 @@ describe('Oidc Database', () => {
|
||||
scope: undefined,
|
||||
expiresAt: undefined,
|
||||
metadata: undefined,
|
||||
createdAt: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -94,11 +93,195 @@ describe('Oidc Database', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Authorization Sessions', () => {
|
||||
it('should create and return an authorization session', async () => {
|
||||
const { oidc } = await createOidcDatabase(databaseId);
|
||||
|
||||
const client = await oidc.createClient({
|
||||
clientId: 'test-client',
|
||||
clientName: 'Test Client',
|
||||
clientSecret: 'test-secret',
|
||||
redirectUris: ['https://example.com/callback'],
|
||||
responseTypes: ['code'],
|
||||
grantTypes: ['authorization_code'],
|
||||
});
|
||||
|
||||
const session = await oidc.createAuthorizationSession({
|
||||
id: 'test-session',
|
||||
clientId: client.clientId,
|
||||
userEntityRef: 'user:default/blam',
|
||||
redirectUri: 'https://example.com/callback',
|
||||
responseType: 'code',
|
||||
scope: 'openid',
|
||||
state: 'test-state',
|
||||
codeChallenge: 'test-challenge',
|
||||
codeChallengeMethod: 'S256',
|
||||
nonce: 'test-nonce',
|
||||
expiresAt: '2025-01-01T00:00:00Z',
|
||||
});
|
||||
|
||||
expect(session).toEqual(
|
||||
expect.objectContaining({
|
||||
id: 'test-session',
|
||||
clientId: client.clientId,
|
||||
userEntityRef: 'user:default/blam',
|
||||
redirectUri: 'https://example.com/callback',
|
||||
responseType: 'code',
|
||||
scope: 'openid',
|
||||
state: 'test-state',
|
||||
codeChallenge: 'test-challenge',
|
||||
codeChallengeMethod: 'S256',
|
||||
nonce: 'test-nonce',
|
||||
expiresAt: '2025-01-01T00:00:00Z',
|
||||
status: 'pending',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should allow updating the authorization session', async () => {
|
||||
const { oidc } = await createOidcDatabase(databaseId);
|
||||
|
||||
const client = await oidc.createClient({
|
||||
clientId: 'test-client',
|
||||
clientName: 'Test Client',
|
||||
clientSecret: 'test-secret',
|
||||
redirectUris: ['https://example.com/callback'],
|
||||
responseTypes: ['code'],
|
||||
grantTypes: ['authorization_code'],
|
||||
});
|
||||
|
||||
const session = await oidc.createAuthorizationSession({
|
||||
id: 'test-session',
|
||||
clientId: client.clientId,
|
||||
redirectUri: 'https://example.com/callback',
|
||||
responseType: 'code',
|
||||
expiresAt: '2025-01-01T00:00:00Z',
|
||||
});
|
||||
|
||||
await expect(
|
||||
oidc.updateAuthorizationSession({
|
||||
id: 'test-session',
|
||||
userEntityRef: 'user:default/blam',
|
||||
status: 'approved',
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
...session,
|
||||
userEntityRef: 'user:default/blam',
|
||||
status: 'approved',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Consent Requests', () => {
|
||||
it('should create and return a consent request', async () => {
|
||||
const { oidc } = await createOidcDatabase(databaseId);
|
||||
|
||||
const client = await oidc.createClient({
|
||||
clientId: 'test-client',
|
||||
clientName: 'Test Client',
|
||||
clientSecret: 'test-secret',
|
||||
redirectUris: ['https://example.com/callback'],
|
||||
responseTypes: ['code'],
|
||||
grantTypes: ['authorization_code'],
|
||||
});
|
||||
|
||||
const session = await oidc.createAuthorizationSession({
|
||||
id: 'test-session',
|
||||
clientId: client.clientId,
|
||||
redirectUri: 'https://example.com/callback',
|
||||
responseType: 'code',
|
||||
expiresAt: '2025-01-01T00:00:00Z',
|
||||
});
|
||||
|
||||
const consentRequest = await oidc.createConsentRequest({
|
||||
id: 'test-consent',
|
||||
sessionId: session.id,
|
||||
expiresAt: '2025-01-01T00:00:00Z',
|
||||
});
|
||||
|
||||
await expect(
|
||||
oidc.getConsentRequest({ id: 'test-consent' }),
|
||||
).resolves.toEqual(consentRequest);
|
||||
});
|
||||
|
||||
it('should return consent request with session data', async () => {
|
||||
const { oidc } = await createOidcDatabase(databaseId);
|
||||
|
||||
const client = await oidc.createClient({
|
||||
clientId: 'test-client',
|
||||
clientName: 'Test Client',
|
||||
clientSecret: 'test-secret',
|
||||
redirectUris: ['https://example.com/callback'],
|
||||
responseTypes: ['code'],
|
||||
grantTypes: ['authorization_code'],
|
||||
});
|
||||
|
||||
const session = await oidc.createAuthorizationSession({
|
||||
id: 'test-session',
|
||||
clientId: client.clientId,
|
||||
redirectUri: 'https://example.com/callback',
|
||||
responseType: 'code',
|
||||
scope: 'openid',
|
||||
state: 'test-state',
|
||||
expiresAt: '2025-01-01T00:00:00Z',
|
||||
});
|
||||
|
||||
const consentRequest = await oidc.createConsentRequest({
|
||||
id: 'test-consent',
|
||||
sessionId: session.id,
|
||||
expiresAt: '2025-01-01T00:00:00Z',
|
||||
});
|
||||
|
||||
const consentFromDb = await oidc.getConsentRequest({
|
||||
id: 'test-consent',
|
||||
});
|
||||
const sessionFromDb = await oidc.getAuthorizationSession({
|
||||
id: consentFromDb!.sessionId,
|
||||
});
|
||||
|
||||
expect(consentFromDb).toEqual(consentRequest);
|
||||
expect(sessionFromDb).toEqual(session);
|
||||
});
|
||||
|
||||
it('should delete consent request', async () => {
|
||||
const { oidc } = await createOidcDatabase(databaseId);
|
||||
|
||||
const client = await oidc.createClient({
|
||||
clientId: 'test-client',
|
||||
clientName: 'Test Client',
|
||||
clientSecret: 'test-secret',
|
||||
redirectUris: ['https://example.com/callback'],
|
||||
responseTypes: ['code'],
|
||||
grantTypes: ['authorization_code'],
|
||||
});
|
||||
|
||||
const session = await oidc.createAuthorizationSession({
|
||||
id: 'test-session',
|
||||
clientId: client.clientId,
|
||||
redirectUri: 'https://example.com/callback',
|
||||
responseType: 'code',
|
||||
expiresAt: '2025-01-01T00:00:00Z',
|
||||
});
|
||||
|
||||
await oidc.createConsentRequest({
|
||||
id: 'test-consent',
|
||||
sessionId: session.id,
|
||||
expiresAt: '2025-01-01T00:00:00Z',
|
||||
});
|
||||
|
||||
await oidc.deleteConsentRequest({ id: 'test-consent' });
|
||||
|
||||
await expect(
|
||||
oidc.getConsentRequest({ id: 'test-consent' }),
|
||||
).resolves.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Authorization Codes', () => {
|
||||
it('should create and return an authorization code', async () => {
|
||||
const { oidc } = await createOidcDatabase(databaseId);
|
||||
|
||||
const mockClient = await oidc.createClient({
|
||||
const client = await oidc.createClient({
|
||||
clientId: 'test-client',
|
||||
clientName: 'Test Client',
|
||||
clientSecret: 'test-secret',
|
||||
@@ -107,35 +290,33 @@ describe('Oidc Database', () => {
|
||||
grantTypes: ['authorization_code'],
|
||||
});
|
||||
|
||||
const authorizationCode = await oidc.createAuthorizationCode({
|
||||
code: 'test-code',
|
||||
clientId: mockClient.clientId,
|
||||
userEntityRef: 'user:default/blam',
|
||||
const session = await oidc.createAuthorizationSession({
|
||||
id: 'test-session',
|
||||
clientId: client.clientId,
|
||||
redirectUri: 'https://example.com/callback',
|
||||
scope: undefined,
|
||||
codeChallenge: 'test-challenge',
|
||||
codeChallengeMethod: 'S256',
|
||||
nonce: 'test-nonce',
|
||||
expiresAt: '2025-01-01',
|
||||
responseType: 'code',
|
||||
expiresAt: '2025-01-01T00:00:00Z',
|
||||
});
|
||||
|
||||
await expect(
|
||||
oidc.getAuthorizationCode({ code: 'test-code' }),
|
||||
).resolves.toEqual(authorizationCode);
|
||||
const authCode = await oidc.createAuthorizationCode({
|
||||
code: 'test-code',
|
||||
sessionId: session.id,
|
||||
expiresAt: '2025-01-01T00:00:00Z',
|
||||
});
|
||||
|
||||
expect(authCode).toEqual(
|
||||
expect.objectContaining({
|
||||
code: 'test-code',
|
||||
sessionId: session.id,
|
||||
expiresAt: '2025-01-01T00:00:00Z',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should return null if the authorization code does not exist', async () => {
|
||||
it('should return authorization code with session data', async () => {
|
||||
const { oidc } = await createOidcDatabase(databaseId);
|
||||
|
||||
await expect(
|
||||
oidc.getAuthorizationCode({ code: 'test-code' }),
|
||||
).resolves.toBeNull();
|
||||
});
|
||||
|
||||
it('should return the authorization code when created', async () => {
|
||||
const { oidc } = await createOidcDatabase(databaseId);
|
||||
|
||||
const mockClient = await oidc.createClient({
|
||||
const client = await oidc.createClient({
|
||||
clientId: 'test-client',
|
||||
clientName: 'Test Client',
|
||||
clientSecret: 'test-secret',
|
||||
@@ -144,27 +325,40 @@ describe('Oidc Database', () => {
|
||||
grantTypes: ['authorization_code'],
|
||||
});
|
||||
|
||||
const authorizationCode = await oidc.createAuthorizationCode({
|
||||
code: 'test-code',
|
||||
clientId: mockClient.clientId,
|
||||
const session = await oidc.createAuthorizationSession({
|
||||
id: 'test-session',
|
||||
clientId: client.clientId,
|
||||
userEntityRef: 'user:default/blam',
|
||||
redirectUri: 'https://example.com/callback',
|
||||
scope: undefined,
|
||||
responseType: 'code',
|
||||
scope: 'openid',
|
||||
codeChallenge: 'test-challenge',
|
||||
codeChallengeMethod: 'S256',
|
||||
nonce: 'test-nonce',
|
||||
expiresAt: '2025-01-01',
|
||||
expiresAt: '2025-01-01T00:00:00Z',
|
||||
});
|
||||
|
||||
await expect(
|
||||
oidc.getAuthorizationCode({ code: 'test-code' }),
|
||||
).resolves.toEqual(authorizationCode);
|
||||
const authCode = await oidc.createAuthorizationCode({
|
||||
code: 'test-code',
|
||||
sessionId: session.id,
|
||||
expiresAt: '2025-01-01T00:00:00Z',
|
||||
});
|
||||
|
||||
const authCodeFromDb = await oidc.getAuthorizationCode({
|
||||
code: 'test-code',
|
||||
});
|
||||
const sessionFromDb = await oidc.getAuthorizationSession({
|
||||
id: authCodeFromDb!.sessionId,
|
||||
});
|
||||
|
||||
expect(authCodeFromDb).toEqual(authCode);
|
||||
expect(sessionFromDb).toEqual(session);
|
||||
});
|
||||
|
||||
it('should allow updating the authorization code', async () => {
|
||||
const { oidc } = await createOidcDatabase(databaseId);
|
||||
|
||||
const mockClient = await oidc.createClient({
|
||||
const client = await oidc.createClient({
|
||||
clientId: 'test-client',
|
||||
clientName: 'Test Client',
|
||||
clientSecret: 'test-secret',
|
||||
@@ -173,24 +367,27 @@ describe('Oidc Database', () => {
|
||||
grantTypes: ['authorization_code'],
|
||||
});
|
||||
|
||||
const authorizationCode = await oidc.createAuthorizationCode({
|
||||
code: 'test-code',
|
||||
clientId: mockClient.clientId,
|
||||
userEntityRef: 'user:default/blam',
|
||||
const session = await oidc.createAuthorizationSession({
|
||||
id: 'test-session',
|
||||
clientId: client.clientId,
|
||||
redirectUri: 'https://example.com/callback',
|
||||
codeChallenge: 'test-challenge',
|
||||
codeChallengeMethod: 'S256',
|
||||
nonce: 'test-nonce',
|
||||
expiresAt: '2025-01-01',
|
||||
responseType: 'code',
|
||||
expiresAt: '2025-01-01T00:00:00Z',
|
||||
});
|
||||
|
||||
await expect(
|
||||
oidc.updateAuthorizationCode({
|
||||
code: 'test-code',
|
||||
used: true,
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
...authorizationCode,
|
||||
const authCode = await oidc.createAuthorizationCode({
|
||||
code: 'test-code',
|
||||
sessionId: session.id,
|
||||
expiresAt: '2025-01-01T00:00:00Z',
|
||||
});
|
||||
|
||||
const updatedAuthCode = await oidc.updateAuthorizationCode({
|
||||
code: 'test-code',
|
||||
used: true,
|
||||
});
|
||||
|
||||
expect(updatedAuthCode).toEqual({
|
||||
...authCode,
|
||||
used: true,
|
||||
});
|
||||
});
|
||||
@@ -200,7 +397,7 @@ describe('Oidc Database', () => {
|
||||
it('should create and return an access token', async () => {
|
||||
const { oidc } = await createOidcDatabase(databaseId);
|
||||
|
||||
const mockClient = await oidc.createClient({
|
||||
const client = await oidc.createClient({
|
||||
clientId: 'test-client',
|
||||
clientName: 'Test Client',
|
||||
clientSecret: 'test-secret',
|
||||
@@ -209,81 +406,27 @@ describe('Oidc Database', () => {
|
||||
grantTypes: ['authorization_code'],
|
||||
});
|
||||
|
||||
const accessToken = await oidc.createAccessToken({
|
||||
tokenId: 'test-token',
|
||||
clientId: mockClient.clientId,
|
||||
userEntityRef: 'user:default/blam',
|
||||
expiresAt: '2025-01-01',
|
||||
revoked: false,
|
||||
});
|
||||
|
||||
await expect(
|
||||
oidc.getAccessToken({ tokenId: 'test-token' }),
|
||||
).resolves.toEqual(accessToken);
|
||||
});
|
||||
|
||||
it('should return null if the access token does not exist', async () => {
|
||||
const { oidc } = await createOidcDatabase(databaseId);
|
||||
|
||||
await expect(
|
||||
oidc.getAccessToken({ tokenId: 'test-token' }),
|
||||
).resolves.toBeNull();
|
||||
});
|
||||
|
||||
it('should return the access token when created', async () => {
|
||||
const { oidc } = await createOidcDatabase(databaseId);
|
||||
|
||||
const mockClient = await oidc.createClient({
|
||||
clientId: 'test-client',
|
||||
clientName: 'Test Client',
|
||||
clientSecret: 'test-secret',
|
||||
redirectUris: ['https://example.com/callback'],
|
||||
responseTypes: ['code'],
|
||||
grantTypes: ['authorization_code'],
|
||||
const session = await oidc.createAuthorizationSession({
|
||||
id: 'test-session',
|
||||
clientId: client.clientId,
|
||||
redirectUri: 'https://example.com/callback',
|
||||
responseType: 'code',
|
||||
expiresAt: '2025-01-01T00:00:00Z',
|
||||
});
|
||||
|
||||
const accessToken = await oidc.createAccessToken({
|
||||
tokenId: 'test-token',
|
||||
clientId: mockClient.clientId,
|
||||
userEntityRef: 'user:default/blam',
|
||||
expiresAt: '2025-01-01',
|
||||
revoked: false,
|
||||
sessionId: session.id,
|
||||
expiresAt: '2025-01-01T00:00:00Z',
|
||||
});
|
||||
|
||||
await expect(
|
||||
oidc.getAccessToken({ tokenId: 'test-token' }),
|
||||
).resolves.toEqual(accessToken);
|
||||
});
|
||||
|
||||
it('should allow updating the access token', async () => {
|
||||
const { oidc } = await createOidcDatabase(databaseId);
|
||||
|
||||
const mockClient = await oidc.createClient({
|
||||
clientId: 'test-client',
|
||||
clientName: 'Test Client',
|
||||
clientSecret: 'test-secret',
|
||||
redirectUris: ['https://example.com/callback'],
|
||||
responseTypes: ['code'],
|
||||
grantTypes: ['authorization_code'],
|
||||
});
|
||||
|
||||
const accessToken = await oidc.createAccessToken({
|
||||
tokenId: 'test-token',
|
||||
clientId: mockClient.clientId,
|
||||
userEntityRef: 'user:default/blam',
|
||||
expiresAt: '2025-01-01',
|
||||
revoked: false,
|
||||
});
|
||||
|
||||
await expect(
|
||||
oidc.updateAccessToken({
|
||||
expect(accessToken).toEqual(
|
||||
expect.objectContaining({
|
||||
tokenId: 'test-token',
|
||||
revoked: true,
|
||||
sessionId: session.id,
|
||||
expiresAt: '2025-01-01T00:00:00Z',
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
...accessToken,
|
||||
revoked: true,
|
||||
});
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,13 +16,10 @@
|
||||
import { Knex } from 'knex';
|
||||
import { AuthDatabase } from './AuthDatabase';
|
||||
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
type OidcClientRow = {
|
||||
client_id: string;
|
||||
client_secret: string;
|
||||
client_name: string;
|
||||
created_at: string;
|
||||
expires_at: string | null;
|
||||
response_types: string;
|
||||
grant_types: string;
|
||||
@@ -31,31 +28,41 @@ type OidcClientRow = {
|
||||
metadata: string | null;
|
||||
};
|
||||
|
||||
type OidcAuthorizationCodeRow = {
|
||||
code: string;
|
||||
type OAuthAuthorizationSessionRow = {
|
||||
id: string;
|
||||
client_id: string;
|
||||
user_entity_ref: string;
|
||||
user_entity_ref: string | null;
|
||||
redirect_uri: string;
|
||||
scope: string | null;
|
||||
state: string | null;
|
||||
response_type: string;
|
||||
code_challenge: string | null;
|
||||
code_challenge_method: string | null;
|
||||
nonce: string | null;
|
||||
created_at: string;
|
||||
status: 'pending' | 'approved' | 'rejected' | 'expired';
|
||||
expires_at: string;
|
||||
used?: boolean;
|
||||
};
|
||||
|
||||
type OidcConsentRequestRow = {
|
||||
id: string;
|
||||
session_id: string;
|
||||
expires_at: string;
|
||||
};
|
||||
|
||||
type OidcAuthorizationCodeRow = {
|
||||
code: string;
|
||||
session_id: string;
|
||||
expires_at: string;
|
||||
used: boolean;
|
||||
};
|
||||
|
||||
type OidcAccessTokenRow = {
|
||||
token_id: string;
|
||||
client_id: string;
|
||||
user_entity_ref: string;
|
||||
scope: string | null;
|
||||
created_at: string;
|
||||
session_id: string;
|
||||
expires_at: string;
|
||||
revoked?: boolean;
|
||||
};
|
||||
|
||||
type Client = {
|
||||
export type Client = {
|
||||
clientId: string;
|
||||
clientName: string;
|
||||
clientSecret: string;
|
||||
@@ -65,31 +72,40 @@ type Client = {
|
||||
scope?: string;
|
||||
expiresAt?: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
createdAt: string;
|
||||
};
|
||||
|
||||
type AuthorizationCode = {
|
||||
code: string;
|
||||
export type AuthorizationSession = {
|
||||
id: string;
|
||||
clientId: string;
|
||||
userEntityRef: string;
|
||||
userEntityRef?: string;
|
||||
redirectUri: string;
|
||||
scope?: string;
|
||||
state?: string;
|
||||
responseType: string;
|
||||
codeChallenge?: string;
|
||||
codeChallengeMethod?: string;
|
||||
nonce?: string;
|
||||
createdAt: string;
|
||||
status: 'pending' | 'approved' | 'rejected' | 'expired';
|
||||
expiresAt: string;
|
||||
};
|
||||
|
||||
export type ConsentRequest = {
|
||||
id: string;
|
||||
sessionId: string;
|
||||
expiresAt: string;
|
||||
};
|
||||
|
||||
export type AuthorizationCode = {
|
||||
code: string;
|
||||
sessionId: string;
|
||||
expiresAt: string;
|
||||
used: boolean;
|
||||
};
|
||||
|
||||
type AccessToken = {
|
||||
export type AccessToken = {
|
||||
tokenId: string;
|
||||
clientId: string;
|
||||
userEntityRef: string;
|
||||
scope?: string;
|
||||
createdAt: string;
|
||||
sessionId: string;
|
||||
expiresAt: string;
|
||||
revoked?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -104,14 +120,11 @@ export class OidcDatabase {
|
||||
return new OidcDatabase(client);
|
||||
}
|
||||
|
||||
async createClient(client: Omit<Client, 'createdAt'>) {
|
||||
const now = DateTime.now().toString();
|
||||
|
||||
async createClient(client: Client) {
|
||||
await this.db<OidcClientRow>('oidc_clients').insert({
|
||||
client_id: client.clientId,
|
||||
client_secret: client.clientSecret,
|
||||
client_name: client.clientName,
|
||||
created_at: now,
|
||||
expires_at: client.expiresAt,
|
||||
response_types: JSON.stringify(client.responseTypes),
|
||||
grant_types: JSON.stringify(client.grantTypes),
|
||||
@@ -120,10 +133,7 @@ export class OidcDatabase {
|
||||
metadata: JSON.stringify(client.metadata),
|
||||
});
|
||||
|
||||
return {
|
||||
...client,
|
||||
createdAt: now,
|
||||
};
|
||||
return client;
|
||||
}
|
||||
|
||||
async getClient({ clientId }: { clientId: string }) {
|
||||
@@ -138,44 +148,122 @@ export class OidcDatabase {
|
||||
return this.rowToClient(client) as Client;
|
||||
}
|
||||
|
||||
async createAuthorizationCode(
|
||||
authorizationCode: Omit<AuthorizationCode, 'createdAt' | 'used'>,
|
||||
async createAuthorizationSession(
|
||||
session: Omit<AuthorizationSession, 'status'>,
|
||||
) {
|
||||
const now = DateTime.now().toString();
|
||||
await this.db<OAuthAuthorizationSessionRow>(
|
||||
'oauth_authorization_sessions',
|
||||
).insert({
|
||||
id: session.id,
|
||||
client_id: session.clientId,
|
||||
user_entity_ref: session.userEntityRef,
|
||||
redirect_uri: session.redirectUri,
|
||||
scope: session.scope,
|
||||
state: session.state,
|
||||
response_type: session.responseType,
|
||||
code_challenge: session.codeChallenge,
|
||||
code_challenge_method: session.codeChallengeMethod,
|
||||
nonce: session.nonce,
|
||||
status: 'pending',
|
||||
expires_at: session.expiresAt,
|
||||
});
|
||||
|
||||
return {
|
||||
...session,
|
||||
status: 'pending',
|
||||
};
|
||||
}
|
||||
|
||||
async updateAuthorizationSession(
|
||||
session: Partial<AuthorizationSession> & { id: string },
|
||||
) {
|
||||
const row = this.authorizationSessionToRow(session);
|
||||
const updatedFields = Object.fromEntries(
|
||||
Object.entries(row).filter(([_, value]) => value !== undefined),
|
||||
);
|
||||
|
||||
const [updated] = await this.db<OAuthAuthorizationSessionRow>(
|
||||
'oauth_authorization_sessions',
|
||||
)
|
||||
.where('id', session.id)
|
||||
.update(updatedFields)
|
||||
.returning('*');
|
||||
|
||||
return this.rowToAuthorizationSession(updated) as AuthorizationSession;
|
||||
}
|
||||
|
||||
async createConsentRequest(consentRequest: ConsentRequest) {
|
||||
await this.db<OidcConsentRequestRow>('oidc_consent_requests').insert({
|
||||
id: consentRequest.id,
|
||||
session_id: consentRequest.sessionId,
|
||||
expires_at: consentRequest.expiresAt,
|
||||
});
|
||||
|
||||
return consentRequest;
|
||||
}
|
||||
|
||||
async getConsentRequest({ id }: { id: string }) {
|
||||
const consentRequest = await this.db<OidcConsentRequestRow>(
|
||||
'oidc_consent_requests',
|
||||
)
|
||||
.where('id', id)
|
||||
.first();
|
||||
|
||||
if (!consentRequest) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.rowToConsentRequest(consentRequest) as ConsentRequest;
|
||||
}
|
||||
|
||||
async getAuthorizationSession({ id }: { id: string }) {
|
||||
const session = await this.db<OAuthAuthorizationSessionRow>(
|
||||
'oauth_authorization_sessions',
|
||||
)
|
||||
.where('id', id)
|
||||
.first();
|
||||
|
||||
if (!session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.rowToAuthorizationSession(session) as AuthorizationSession;
|
||||
}
|
||||
|
||||
async deleteConsentRequest({ id }: { id: string }) {
|
||||
await this.db<OidcConsentRequestRow>('oidc_consent_requests')
|
||||
.where('id', id)
|
||||
.delete();
|
||||
}
|
||||
|
||||
async createAuthorizationCode(
|
||||
authorizationCode: Omit<AuthorizationCode, 'used'>,
|
||||
) {
|
||||
await this.db<OidcAuthorizationCodeRow>('oidc_authorization_codes').insert({
|
||||
code: authorizationCode.code,
|
||||
client_id: authorizationCode.clientId,
|
||||
user_entity_ref: authorizationCode.userEntityRef,
|
||||
redirect_uri: authorizationCode.redirectUri,
|
||||
scope: authorizationCode.scope,
|
||||
code_challenge: authorizationCode.codeChallenge,
|
||||
code_challenge_method: authorizationCode.codeChallengeMethod,
|
||||
nonce: authorizationCode.nonce,
|
||||
session_id: authorizationCode.sessionId,
|
||||
expires_at: authorizationCode.expiresAt,
|
||||
created_at: now,
|
||||
used: false,
|
||||
});
|
||||
|
||||
return {
|
||||
...authorizationCode,
|
||||
createdAt: now,
|
||||
used: false,
|
||||
};
|
||||
}
|
||||
|
||||
async getAuthorizationCode({ code }: { code: string }) {
|
||||
const authorizationCode = await this.db<OidcAuthorizationCodeRow>(
|
||||
const authCode = await this.db<OidcAuthorizationCodeRow>(
|
||||
'oidc_authorization_codes',
|
||||
)
|
||||
.where('code', code)
|
||||
.first();
|
||||
|
||||
if (!authorizationCode) {
|
||||
if (!authCode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.rowToAuthorizationCode(authorizationCode) as AuthorizationCode;
|
||||
return this.rowToAuthorizationCode(authCode) as AuthorizationCode;
|
||||
}
|
||||
|
||||
async updateAuthorizationCode(
|
||||
@@ -196,50 +284,14 @@ export class OidcDatabase {
|
||||
return this.rowToAuthorizationCode(updated) as AuthorizationCode;
|
||||
}
|
||||
|
||||
async createAccessToken(accessToken: Omit<AccessToken, 'createdAt'>) {
|
||||
const now = DateTime.now().toString();
|
||||
|
||||
async createAccessToken(accessToken: AccessToken) {
|
||||
await this.db<OidcAccessTokenRow>('oidc_access_tokens').insert({
|
||||
token_id: accessToken.tokenId,
|
||||
client_id: accessToken.clientId,
|
||||
user_entity_ref: accessToken.userEntityRef,
|
||||
scope: accessToken.scope,
|
||||
created_at: now,
|
||||
session_id: accessToken.sessionId,
|
||||
expires_at: accessToken.expiresAt,
|
||||
revoked: accessToken.revoked ?? false,
|
||||
});
|
||||
|
||||
return {
|
||||
...accessToken,
|
||||
createdAt: now,
|
||||
};
|
||||
}
|
||||
|
||||
async getAccessToken({ tokenId }: { tokenId: string }) {
|
||||
const accessToken = await this.db<OidcAccessTokenRow>('oidc_access_tokens')
|
||||
.where('token_id', tokenId)
|
||||
.first();
|
||||
|
||||
if (!accessToken) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.rowToAccessToken(accessToken) as AccessToken;
|
||||
}
|
||||
|
||||
async updateAccessToken(
|
||||
accessToken: Partial<AccessToken> & { tokenId: string },
|
||||
) {
|
||||
const row = this.accessTokenToRow(accessToken);
|
||||
const updatedFields = Object.fromEntries(
|
||||
Object.entries(row).filter(([_, value]) => value !== undefined),
|
||||
);
|
||||
const [updated] = await this.db<OidcAccessTokenRow>('oidc_access_tokens')
|
||||
.where('token_id', accessToken.tokenId)
|
||||
.update(updatedFields)
|
||||
.returning('*');
|
||||
|
||||
return this.rowToAccessToken(updated) as AccessToken;
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
private rowToClient(row: Partial<OidcClientRow>): Partial<Client> {
|
||||
@@ -257,7 +309,54 @@ export class OidcDatabase {
|
||||
scope: row.scope ?? undefined,
|
||||
expiresAt: row.expires_at ?? undefined,
|
||||
metadata: row.metadata ? JSON.parse(row.metadata) : undefined,
|
||||
createdAt: row.created_at,
|
||||
};
|
||||
}
|
||||
|
||||
private authorizationSessionToRow(
|
||||
session: Partial<AuthorizationSession>,
|
||||
): Partial<OAuthAuthorizationSessionRow> {
|
||||
return {
|
||||
id: session.id,
|
||||
client_id: session.clientId,
|
||||
user_entity_ref: session.userEntityRef,
|
||||
redirect_uri: session.redirectUri,
|
||||
scope: session.scope,
|
||||
state: session.state,
|
||||
response_type: session.responseType,
|
||||
code_challenge: session.codeChallenge,
|
||||
code_challenge_method: session.codeChallengeMethod,
|
||||
nonce: session.nonce,
|
||||
status: session.status,
|
||||
expires_at: session.expiresAt,
|
||||
};
|
||||
}
|
||||
|
||||
private rowToAuthorizationSession(
|
||||
row: Partial<OAuthAuthorizationSessionRow>,
|
||||
): Partial<AuthorizationSession> {
|
||||
return {
|
||||
id: row.id,
|
||||
clientId: row.client_id,
|
||||
userEntityRef: row.user_entity_ref ?? undefined,
|
||||
redirectUri: row.redirect_uri,
|
||||
scope: row.scope ?? undefined,
|
||||
state: row.state ?? undefined,
|
||||
responseType: row.response_type,
|
||||
codeChallenge: row.code_challenge ?? undefined,
|
||||
codeChallengeMethod: row.code_challenge_method ?? undefined,
|
||||
nonce: row.nonce ?? undefined,
|
||||
status: row.status,
|
||||
expiresAt: row.expires_at,
|
||||
};
|
||||
}
|
||||
|
||||
private rowToConsentRequest(
|
||||
row: Partial<OidcConsentRequestRow>,
|
||||
): Partial<ConsentRequest> {
|
||||
return {
|
||||
id: row.id,
|
||||
sessionId: row.session_id,
|
||||
expiresAt: row.expires_at,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -266,14 +365,7 @@ export class OidcDatabase {
|
||||
): Partial<OidcAuthorizationCodeRow> {
|
||||
return {
|
||||
code: authorizationCode.code,
|
||||
client_id: authorizationCode.clientId,
|
||||
user_entity_ref: authorizationCode.userEntityRef,
|
||||
redirect_uri: authorizationCode.redirectUri,
|
||||
scope: authorizationCode.scope,
|
||||
code_challenge: authorizationCode.codeChallenge,
|
||||
code_challenge_method: authorizationCode.codeChallengeMethod,
|
||||
nonce: authorizationCode.nonce,
|
||||
created_at: authorizationCode.createdAt,
|
||||
session_id: authorizationCode.sessionId,
|
||||
expires_at: authorizationCode.expiresAt,
|
||||
used: authorizationCode.used,
|
||||
};
|
||||
@@ -284,44 +376,9 @@ export class OidcDatabase {
|
||||
): Partial<AuthorizationCode> {
|
||||
return {
|
||||
code: row.code,
|
||||
clientId: row.client_id,
|
||||
userEntityRef: row.user_entity_ref,
|
||||
redirectUri: row.redirect_uri,
|
||||
scope: row.scope ?? undefined,
|
||||
codeChallenge: row.code_challenge ?? undefined,
|
||||
codeChallengeMethod: row.code_challenge_method ?? undefined,
|
||||
nonce: row.nonce ?? undefined,
|
||||
createdAt: row.created_at,
|
||||
sessionId: row.session_id,
|
||||
expiresAt: row.expires_at,
|
||||
used: Boolean(row.used),
|
||||
};
|
||||
}
|
||||
|
||||
private accessTokenToRow(
|
||||
accessToken: Partial<AccessToken>,
|
||||
): Partial<OidcAccessTokenRow> {
|
||||
return {
|
||||
token_id: accessToken.tokenId,
|
||||
client_id: accessToken.clientId,
|
||||
user_entity_ref: accessToken.userEntityRef,
|
||||
scope: accessToken.scope,
|
||||
created_at: accessToken.createdAt,
|
||||
expires_at: accessToken.expiresAt,
|
||||
revoked: accessToken.revoked,
|
||||
};
|
||||
}
|
||||
|
||||
private rowToAccessToken(
|
||||
row: Partial<OidcAccessTokenRow>,
|
||||
): Partial<AccessToken> {
|
||||
return {
|
||||
tokenId: row.token_id,
|
||||
clientId: row.client_id,
|
||||
userEntityRef: row.user_entity_ref,
|
||||
scope: row.scope ?? undefined,
|
||||
createdAt: row.created_at,
|
||||
expiresAt: row.expires_at,
|
||||
revoked: Boolean(row.revoked),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user