Skip to content

feat: near wallet login#237

Merged
think-in-universe merged 20 commits intomainfrom
near_login
Dec 12, 2025
Merged

feat: near wallet login#237
think-in-universe merged 20 commits intomainfrom
near_login

Conversation

@danielwpz
Copy link
Contributor

@danielwpz danielwpz commented Dec 9, 2025

Note

Adds NEAR wallet authentication endpoint with NEP-413 verification, DB-backed nonce replay protection, config/env wiring, and E2E tests.

  • API:
    • Add POST /v1/auth/near route and wire via build_auth_routes; pass AuthComponents and include near_auth_service in AuthComponents.
    • New request/response DTOs for NEAR auth; header validation (User-Agent); error mapping.
  • Services (Auth):
    • Implement NearAuthService (NEP-413): validates recipient/message, nonce timestamp (5‑min window), verifies signature via near-api, consumes nonce to prevent replay, then creates session.
    • Expose NearNonceRepository port; integrate with existing AuthServiceTrait.
  • Database:
    • Migration V0033__add_near_nonces.sql creating near_used_nonces table + index.
    • New PostgresNearNonceRepository with consume_nonce and cleanup.
  • Config:
    • Add NearConfig { rpc_url, expected_recipient } to AuthConfig; load from env (NEAR_EXPECTED_RECIPIENT, optional NEAR_RPC_URL).
    • Update examples and tests to include defaults.
  • Tests:
    • Add e2e_near_auth covering invalid signatures, User‑Agent checks, nonce window, invalid message/recipient, malformed JSON, zero‑timestamp nonce.
    • Helpers for constructing NEP‑413 payloads/nonces.
  • Dependencies:
    • Add near-api, base64; lockfile updates and minor crate version pinning (e.g., hmac, sha2, secp256k1).

Written by Cursor Bugbot for commit 516db68. This will update automatically on new commits. Configure here.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is being reviewed by Cursor Bugbot

Details

Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

@think-in-universe
Copy link
Contributor

@claude review

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds NEAR wallet authentication using the NEP-413 signature standard, implementing a complete authentication flow with nonce replay protection, timestamp validation, and database-backed session management.

Key Changes:

  • Implements NEP-413 signature verification with RPC-based public key ownership validation
  • Adds database-backed nonce replay protection with automatic cleanup of expired nonces
  • Integrates NEAR authentication into the existing OAuth-based auth system with session and token management

Reviewed changes

Copilot reviewed 14 out of 15 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
crates/services/src/auth/near.rs Core NEAR authentication service with NEP-413 verification, nonce validation, and timestamp checks
crates/services/src/auth/ports.rs Adds NearNonceRepository trait for replay protection
crates/services/src/auth/mod.rs Exports NEAR auth service module
crates/database/src/repositories/near_nonce.rs PostgreSQL implementation of nonce repository with INSERT/CONFLICT pattern
crates/database/src/repositories/mod.rs Exports NEAR nonce repository
crates/database/src/migrations/sql/V0030__add_near_nonces.sql Creates near_used_nonces table with hex validation constraint and cleanup index
crates/database/src/lib.rs Exports PostgreSQL nonce repository for public API
crates/config/src/types.rs Adds NearConfig with RPC URL and expected recipient configuration
crates/api/src/routes/auth.rs POST /v1/auth/near endpoint with request validation and structured error responses
crates/api/src/lib.rs Wires NearAuthService into AuthComponents and route configuration
crates/api/tests/e2e_near_auth.rs E2E tests for input validation, nonce expiration, and message/recipient validation
crates/api/tests/common/mod.rs Test helpers for creating NEP-413 payloads with configurable timestamps
crates/services/Cargo.toml Adds near-api dependency from GitHub
crates/api/Cargo.toml Adds near-api, base64, and rand dependencies
Cargo.lock Locks NEAR-related dependencies and transitive dependencies

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 15 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Self {
rpc_url: env::var("NEAR_RPC_URL")
.unwrap_or_else(|_| "https://free.rpc.fastnear.com".to_string()),
expected_recipient: NEAR_DEFAULT_RECIPIENT.to_string(),
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expected_recipient field is hardcoded to NEAR_DEFAULT_RECIPIENT and cannot be configured via environment variable. This prevents customization for different deployment environments (e.g., staging vs production, different domains). Consider adding an environment variable option like NEAR_EXPECTED_RECIPIENT that falls back to the default if not set, similar to how rpc_url is handled.

Suggested change
expected_recipient: NEAR_DEFAULT_RECIPIENT.to_string(),
expected_recipient: env::var("NEAR_EXPECTED_RECIPIENT")
.unwrap_or_else(|_| NEAR_DEFAULT_RECIPIENT.to_string()),

Copilot uses AI. Check for mistakes.
const EXPECTED_MESSAGE: &str = "Sign in to NEAR AI Cloud";

/// Custom error type for NEAR authentication
#[derive(Debug)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use thiserror. Example:

// Error types
#[derive(Debug, thiserror::Error)]
pub enum AuthError {
#[error("OAuth error: {0}")]
OAuthError(String),
#[error("Invalid state parameter")]
InvalidState,
#[error("Authentication failed: {0}")]
AuthFailed(String),
#[error("Configuration error: {0}")]
ConfigError(String),
#[error("Network error: {0}")]
NetworkError(String),
#[error("Session not found")]
SessionNotFound,
#[error("User not found")]
UserNotFound,
#[error("Internal error: {0}")]
InternalError(String),
#[error("Unauthorized")]
Unauthorized,
#[error("Invalid user agent")]
InvalidUserAgent,
#[error("User agent is too long (max {0} chars)")]
UserAgentTooLong(usize),
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

Self::validate_message(&payload.message).map_err(|e| anyhow::anyhow!(e))?;

// 3. Cleanup expired nonces
self.cleanup_nonces().await;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that should be a periodic process. Not on every requests?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually it's a balance between optimization and code simplicity, since we have the index INDEX idx_near_used_nonces_used_at ON near_used_nonces(used_at), deletion won't cause a full table scan.
So the cleanup should be very effective each call. (Imagine we have 10k new NEAR auth each day, the deletion call will only touch about 20 rows each time)

Comment on lines 252 to 253
1,
7 * 24,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be consts

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

status,
Json(serde_json::json!({
"error": error_type,
"error_description": error_msg
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returns internal error?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't leak internal error details.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@danielwpz danielwpz temporarily deployed to Cloud API test env December 12, 2025 09:42 — with GitHub Actions Inactive
@think-in-universe think-in-universe dismissed PierreLeGuen’s stale review December 12, 2025 09:56

The comments has been resolved.

@think-in-universe think-in-universe merged commit 0e643b2 into main Dec 12, 2025
2 checks passed
@think-in-universe think-in-universe deleted the near_login branch December 12, 2025 09:58
@PierreLeGuen PierreLeGuen linked an issue Dec 16, 2025 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: NEAR Wallet login

3 participants