summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPawel Zelawski <pawel.zelawski@outlook.com>2025-04-09 19:12:23 +0200
committerPawel Zelawski <pawel.zelawski@outlook.com>2025-04-09 19:12:23 +0200
commit8092ceaf10dd5951d0b5011fc8d5a05b49335a6e (patch)
treedc2400fb38245b51270609a6715fd6d349d13259 /src
parent753fcaebe44fa1b4e8e6e496fbade9508fac1dc1 (diff)
feat: Implement DigiID URI generation and define core types
- Create initial source directory structure (src/). - Define core interfaces (DigiIDUriOptions, DigiIDCallbackData, etc.) and DigiIDError class in src/types.ts. - Set up main export file src/index.ts. - Implement the generateDigiIDUri function in src/digiid.ts for creating DigiID authentication URIs according to the specification. - Include helper function for generating secure nonces using Node crypto. - Add TSDoc comments for clarity and maintainability.
Diffstat (limited to 'src')
-rw-r--r--src/digiid.ts55
-rw-r--r--src/index.ts2
-rw-r--r--src/types.ts55
3 files changed, 112 insertions, 0 deletions
diff --git a/src/digiid.ts b/src/digiid.ts
new file mode 100644
index 0000000..6322403
--- /dev/null
+++ b/src/digiid.ts
@@ -0,0 +1,55 @@
+import { randomBytes } from 'crypto';
+import { DigiIDUriOptions, DigiIDError } from './types';
+
+/**
+ * Generates a secure random nonce (hex string).
+ * @param length - The number of bytes to generate (default: 16, resulting in 32 hex chars).
+ * @returns A hex-encoded random string.
+ */
+function generateNonce(length = 16): string {
+ return randomBytes(length).toString('hex');
+}
+
+/**
+ * Generates a DigiID authentication URI.
+ *
+ * @param options - Options for URI generation, including the callback URL.
+ * @returns The generated DigiID URI string.
+ * @throws {DigiIDError} If the callback URL is invalid or missing.
+ */
+export function generateDigiIDUri(options: DigiIDUriOptions): string {
+ if (!options.callbackUrl) {
+ throw new DigiIDError('Callback URL is required.');
+ }
+
+ let parsedUrl: URL;
+ try {
+ parsedUrl = new URL(options.callbackUrl);
+ } catch (e) {
+ throw new DigiIDError(`Invalid callback URL: ${(e as Error).message}`);
+ }
+
+ // DigiID spec requires stripping the scheme (http/https)
+ const domainAndPath = parsedUrl.host + parsedUrl.pathname;
+
+ const nonce = options.nonce || generateNonce();
+ const unsecureFlag = options.unsecure ? '1' : '0'; // 1 for http, 0 for https
+
+ // Validate scheme based on unsecure flag
+ if (options.unsecure && parsedUrl.protocol !== 'http:') {
+ throw new DigiIDError('Unsecure flag is true, but callback URL does not use http protocol.');
+ }
+ if (!options.unsecure && parsedUrl.protocol !== 'https:') {
+ throw new DigiIDError('Callback URL must use https protocol unless unsecure flag is set to true.');
+ }
+
+ // Construct the URI
+ // Example: digiid://example.com/callback?x=nonce_value&u=0
+ const uri = `digiid://${domainAndPath}?x=${nonce}&u=${unsecureFlag}`;
+
+ // Clean up potential trailing slash in path if no query params exist (though DigiID always has params)
+ // This check might be redundant given DigiID structure, but good practice
+ // const cleanedUri = uri.endsWith('/') && parsedUrl.search === '' ? uri.slice(0, -1) : uri;
+
+ return uri;
+}
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 0000000..11ea761
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1,2 @@
+export * from './types';
+export * from './digiid'; // We'll add functions here later
diff --git a/src/types.ts b/src/types.ts
new file mode 100644
index 0000000..937cfd2
--- /dev/null
+++ b/src/types.ts
@@ -0,0 +1,55 @@
+/**
+ * Options for generating a DigiID URI.
+ */
+export interface DigiIDUriOptions {
+ /** The full URL that the user's DigiID wallet will send the verification data back to. */
+ callbackUrl: string;
+ /** A unique, unpredictable nonce (number used once) for this authentication request. If not provided, a secure random one might be generated (implementation specific). */
+ nonce?: string;
+ /** Set to true for testing over HTTP (insecure), defaults to false (HTTPS required). */
+ unsecure?: boolean;
+}
+
+/**
+ * Data structure typically received from the DigiID wallet callback.
+ */
+export interface DigiIDCallbackData {
+ /** The DigiByte address used for signing. */
+ address: string;
+ /** The DigiID URI that was originally presented to the user. */
+ uri: string;
+ /** The signature proving ownership of the address, signing the URI. */
+ signature: string;
+}
+
+/**
+ * Options for verifying a DigiID callback.
+ */
+export interface DigiIDVerifyOptions {
+ /** The expected callback URL (or parts of it, like domain/path) that should match the one in the received URI. */
+ expectedCallbackUrl: string | URL;
+ /** The specific nonce that was originally generated for this authentication attempt, to prevent replay attacks. */
+ expectedNonce?: string;
+}
+
+/**
+ * Result of a successful DigiID verification.
+ */
+export interface DigiIDVerificationResult {
+ /** Indicates the verification was successful. */
+ isValid: true;
+ /** The DigiByte address that was successfully verified. */
+ address: string;
+ /** The nonce extracted from the verified URI. */
+ nonce: string;
+}
+
+/**
+ * Represents an error during DigiID processing.
+ */
+export class DigiIDError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'DigiIDError';
+ }
+}