Skip to content

Commit

Permalink
feat(admin-api + sdk): enable auth headers for admin api routes (#354)
Browse files Browse the repository at this point in the history
  • Loading branch information
frdomovic authored Jun 21, 2024
1 parent e273e62 commit 03dc2e8
Show file tree
Hide file tree
Showing 12 changed files with 108 additions and 20 deletions.
34 changes: 22 additions & 12 deletions crates/server/src/admin/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ use serde_json::json;
use tower_sessions::{MemoryStore, SessionManagerLayer};
use tracing::info;

use crate::middleware;

use super::handlers;

#[derive(Debug, Serialize, Deserialize)]
Expand Down Expand Up @@ -47,27 +49,18 @@ pub(crate) fn setup(
let session_layer = SessionManagerLayer::new(session_store).with_secure(false);

let shared_state = Arc::new(AdminState {
store,
store: store.clone(),
keypair: config.identity.clone(),
application_manager,
});

let admin_router = Router::new()
.route("/health", get(health_check_handler))
let protected_router = Router::new()
.route(
"/root-key",
post(handlers::root_keys::create_root_key_handler),
)
.route(
"/request-challenge",
post(handlers::challenge::request_challenge_handler),
)
.route("/install-application", post(install_application_handler))
.route("/applications", get(list_applications_handler))
.route(
"/add-client-key",
post(handlers::add_client_key::add_client_key_handler),
)
.route("/did", get(handlers::fetch_did::fetch_did_handler))
.route("/contexts", post(handlers::context::create_context_handler))
.route(
Expand All @@ -91,7 +84,24 @@ pub(crate) fn setup(
get(handlers::context::get_context_storage_handler),
)
.route("/contexts", get(handlers::context::get_contexts_handler))
.layer(Extension(shared_state))
.layer(middleware::auth::AuthSignatureLayer::new(store))
.layer(Extension(shared_state.clone()));

let unprotected_router = Router::new()
.route("/health", get(health_check_handler))
.route(
"/request-challenge",
post(handlers::challenge::request_challenge_handler),
)
.route(
"/add-client-key",
post(handlers::add_client_key::add_client_key_handler),
)
.layer(Extension(shared_state));

let admin_router = Router::new()
.nest("/", unprotected_router)
.nest("/", protected_router)
.layer(session_layer);

Ok(Some((admin_path, admin_router)))
Expand Down
2 changes: 1 addition & 1 deletion packages/calimero-sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@calimero-is-near/calimero-p2p-sdk",
"version": "0.0.22",
"version": "0.0.23",
"description": "Javascript library to interact with Calimero P2P node",
"type": "module",
"main": "lib/index.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/calimero-sdk/src/api/nodeApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export interface LoginRequest {
walletSignature: String;
payload: Payload;
walletMetadata: WalletMetadata;
contextId: String,
contextId: String;
}

export interface RootKeyRequest {
Expand Down
File renamed without changes.
56 changes: 56 additions & 0 deletions packages/calimero-sdk/src/auth/headers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { unmarshalPrivateKey } from '@libp2p/crypto/keys';
import { PrivateKey } from '@libp2p/interface';
import bs58 from 'bs58';
import { WalletType } from '../api/nodeApi';
import { ClientKey } from '../types/storage';
import { getStorageClientKey } from '../storage/storage';

export interface Header {
[key: string]: string;
}

export async function createAuthHeader(
payload: string,
contextId: string
): Promise<Header | null> {
const privateKey: PrivateKey = await getPrivateKey();

if (!privateKey) {
return null;
}

const encoder = new TextEncoder();
const contentBuff = encoder.encode(payload);

const signingKey = bs58.encode(privateKey.public.bytes);

const hashBuffer = await crypto.subtle.digest('SHA-256', contentBuff);
const hashArray = new Uint8Array(hashBuffer);

const signature = await privateKey.sign(hashArray);
const signatureBase58 = bs58.encode(signature);
const contentBase58 = bs58.encode(hashArray);

const headers: Header = {
wallet_type: JSON.stringify(WalletType.NEAR),
signing_key: signingKey,
signature: signatureBase58,
challenge: contentBase58,
context_id: contextId,
};

return headers;
}

export async function getPrivateKey(): Promise<PrivateKey | null> {
try {
const clientKey: ClientKey | null = getStorageClientKey();
if (!clientKey) {
return null;
}
return await unmarshalPrivateKey(bs58.decode(clientKey.privateKey));
} catch (error) {
console.error('Error extracting private key:', error);
return null;
}
}
2 changes: 2 additions & 0 deletions packages/calimero-sdk/src/auth/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './headers';
export * from './ed25519';
1 change: 1 addition & 0 deletions packages/calimero-sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from './setup';
export * from './components';
export * from './types';
export * from './api/nodeApi';
export * from './auth';
23 changes: 21 additions & 2 deletions packages/calimero-sdk/src/storage/storage.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ClientKey } from '../types/storage';

export const CLIENT_KEY = 'client-key';
export const APP_URL = 'app-url';
export const AUTHORIZED = 'node-authorized';

export const setStorageClientKey = (clientKey: ClientKey) => {
Expand All @@ -10,7 +11,7 @@ export const setStorageClientKey = (clientKey: ClientKey) => {
export const getStorageClientKey = (): ClientKey | null => {
if (typeof window !== 'undefined' && window.localStorage) {
let clientKeystore: ClientKey = JSON.parse(
localStorage.getItem(CLIENT_KEY)!,
localStorage.getItem(CLIENT_KEY)
);
if (clientKeystore) {
return clientKeystore;
Expand All @@ -29,7 +30,7 @@ export const setStorageNodeAuthorized = () => {

export const getStorageNodeAuthorized = (): boolean | null => {
if (typeof window !== 'undefined' && window.localStorage) {
let authorized: boolean = JSON.parse(localStorage.getItem(AUTHORIZED)!);
let authorized: boolean = JSON.parse(localStorage.getItem(AUTHORIZED));
if (authorized) {
return authorized;
}
Expand All @@ -40,3 +41,21 @@ export const getStorageNodeAuthorized = (): boolean | null => {
export const clearNodeAuthorized = () => {
localStorage.removeItem(AUTHORIZED);
};

export const clearAppEndpoint = () => {
localStorage.removeItem(APP_URL);
};

export const setAppEndpointKey = (url: String) => {
localStorage.setItem(APP_URL, JSON.stringify(url));
};

export const getAppEndpointKey = (): String | null => {
if (typeof window !== 'undefined' && window.localStorage) {
let url: String = JSON.parse(localStorage.getItem(APP_URL));
if (url) {
return url;
}
}
return null;
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useCallback, useEffect, useState } from 'react';
import { randomBytes } from 'crypto';
import { getOrCreateKeypair } from '../../crypto/ed25519';
import { getOrCreateKeypair } from '../../auth/ed25519';

import {
useAccount,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { ResponseData } from '../../types/api-response';
import { setStorageNodeAuthorized } from '../../storage/storage';
import { Loading } from '../loading/Loading';
import { getNetworkType } from '../eth/type';
import { getOrCreateKeypair } from '../../crypto/ed25519';
import { getOrCreateKeypair } from '../../auth/ed25519';
import { randomBytes } from 'crypto';

interface MetamaskRootKeyProps {
Expand Down
2 changes: 1 addition & 1 deletion packages/calimero-sdk/src/wallets/NearLogin/NearLogin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from '@near-wallet-selector/core';

import { useWalletSelector } from './WalletSelectorContext';
import { getOrCreateKeypair } from '../../crypto/ed25519';
import { getOrCreateKeypair } from '../../auth/ed25519';
import apiClient from '../../api';
import { ResponseData } from '../../types/api-response';
import { setStorageNodeAuthorized } from '../../storage/storage';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
WalletMetadata,
SignatureMessage,
} from '../../api/nodeApi';
import { getOrCreateKeypair } from '../../crypto/ed25519';
import { getOrCreateKeypair } from '../../auth/ed25519';

type Account = AccountView & {
account_id: string;
Expand Down

0 comments on commit 03dc2e8

Please sign in to comment.