This comprehensive guide covers everything from technical specifications for external integrations to internal security mechanisms within the Neup.Account ecosystem.
| Type | Definition |
|---|---|
| Internal Apps | Applications developed by the Neup Group. |
| External Apps | Applications developed by other users, regardless of the domain they reside on. |
This is required for external apps and internal apps in domains other than neupgroup.com
Before an application can verify a session, it must initiate a handshake to obtain the necessary credentials.
- Endpoint:
GET /bridge/handshake.v1/auth/grant - Purpose: Starts the signing flow and redirects to Neup.Account for authentication.
- Required query params:
appId: Your application ID (encoded in public key).redirectsTo: Your server callback URL.
- Optional query params: Any additional parameters will be forwarded to your
redirectsTounchanged.
- If the user is not signed in, they are redirected to
/auth/start?redirects=…. - After successful sign-in, the handshake redirects the user to your
redirectsToURL and appends:tempToken: A short-lived, one-time token (5 minutes) for server-to-server verification.authType: Eithersignin(existing user) orsignup(new user for this app).
- Your app server must then verify the
tempTokenvia the verification API before granting access.
Example Request:
GET https://neupgroup.com/account/bridge/handshake.v1/auth/grant?appId=YOUR_APP_ID&redirectsTo=https://yourapp.com/auth/callback&state=xyz
Example Redirect:
https://yourapp.com/auth/callback?state=xyz&tempToken=TOKEN_VALUE&authType=signin
External applications interact with Neup.Account primarily through the "Sign" endpoint.
External apps perform a POST request to exchange the tempToken (received via the handshake) for a persistent session and a signed JWT.
Endpoint: POST https://neupgroup.com/account/api.v1/auth/grant
Request Body:
{
"appId": "YOUR_APP_ID",
"tempToken": "TOKEN_FROM_HANDSHAKE"
}Response Properties:
aid: The NeupaccountId.sid: A unique session ID for the external app. (Expires in 7 days)skey: A secure session key for the external app. (Expires in 7 days)jwt: A signed JSON Web Token containing user identity and roles. (Expires in 7 minutes)exp: Expiration timestamp (Unix seconds) for the JWT token only.role: The user's assigned role for this application.per: (Optional) Array of permissions, included if the role has "extra" permissions enabled.
JWT Payload:
{
"aid": "123",
"role": "role-name",
"per": ["perm1", "perm2"], // Included if hasExtra is true
"iat": 123123123,
"exp": 234234234 // Exactly 7 minutes after iat
}External apps perform a GET request to check if their authentication grant is still active.
Endpoint: GET https://neupgroup.com/account/api.v1/auth/grant
Query Parameters:
appId: Your application ID.aid: The account ID.sid: The session ID.skey: The session key.
Response:
{
"success": true,
"aid": "acc_123",
"appId": "app_456",
"expiresOn": "2026-03-20T10:00:00.000Z",
"lastLoggedIn": "2026-03-12T10:00:00.000Z"
}External apps perform a PATCH request to extend the session's expiry (sid, aid, skey) and obtain a new JWT token.
- Session Expiry: Extended to 7 days from the time of refresh.
- JWT Expiry: A new JWT is issued with a 7-minute lifetime.
Endpoint: PATCH https://neupgroup.com/account/api.v1/auth/grant
Request Body:
{
"appId": "YOUR_APP_ID",
"aid": "USER_ACCOUNT_ID",
"sid": "EXTERNAL_SESSION_ID",
"skey": "EXTERNAL_SESSION_KEY"
}Response Properties:
aid: The user's account ID.sid: The session ID.jwt: A fresh signed JWT token (expires in 7 minutes).exp: New expiration timestamp (Unix seconds).role: The user's role.per: (Optional) User permissions.
External applications can fetch the current access snapshot for a user. This call must be made from your server.
Endpoint: GET https://neupgroup.com/account/bridge/api.v1/auth/access
Required Query Parameters:
aid: The user's account ID.sid: The user's external session ID.skey: The user's external session key.
Optional Query Parameters:
appId: Your application ID (can be inferred fromsid).
Response Body:
{
"success": true,
"aid": "123",
"appId": "app_123",
"isInternal": false,
"role": "editor",
"teams": [],
"permissions": ["read:profile", "write:profile"],
"assetPermissions": [
{
"id": "perm_1",
"appId": "app_123",
"recipientId": "123",
"ownerId": "456",
"assetId": "profile_22",
"permission": "edit"
}
],
"timestamp": "2024-03-12T12:00:00.000Z"
}For external apps, PATCH /bridge/api.v1/auth/access still supports permission arrays via add and remove. The route also accepts resourceId and parentOwnerId as aliases for the older assetId and ownerId naming.
For applications directly under the Neup.Account ecosystem, use the internal session API to validate sessions, update device types, and handle logout.
Used to verify if a user's session is still active and update its metadata (e.g., device type, last activity).
- Endpoint:
POST /api.v1/auth/session - Request Body:
{ "aid": "USER_ACCOUNT_ID", "sid": "SESSION_ID", "skey": "SESSION_KEY", "deviceType": "android", // optional "activity": "user_action" // optional } - Privacy & Security: Extends session expiry by 30 days and records the
deviceType.
Invalidates a specific session immediately.
- Endpoint:
DELETE /api.v1/auth/session - Request Body:
{ "aid": "USER_ACCOUNT_ID", "sid": "SESSION_ID", "skey": "SESSION_KEY" }
Applications can retrieve user profile information using the profile endpoint. This endpoint supports both persistent session authentication and one-time tempToken authentication.
Retrieves user information based on the level of access granted.
- Endpoint:
GET /api.v1/profile - Authentication:
- Option A (Persistent Session): Pass
aid,sid, andskeyin the request headers. - Option B (One-time Grant): Pass
tempTokenandappIdas query parameters.
- Option A (Persistent Session): Pass
Request Headers (Option A):
aid: USER_ACCOUNT_ID
sid: EXTERNAL_SESSION_ID
skey: EXTERNAL_SESSION_KEYQuery Parameters (Option B):
tempToken: The token received from the handshake.appId: Your application ID.
Query Parameters (Optional for both):
aid: The account ID of the user whose profile you want to fetch.neupid: The NeupID of the user whose profile you want to fetch.
Privacy Logic:
- Self/Authenticated User: Returns full profile details (emails, phones, DOB, gender, nationality, etc.).
- Other Users: Returns limited public information (
displayName,displayImage,verified,accountType).
Example Response (Full Profile):
{
"success": true,
"profile": {
"aid": "acc_123",
"neupId": "user.name",
"displayName": "John Doe",
"displayImage": "https://...",
"firstName": "John",
"lastName": "Doe",
"gender": "male",
"dob": "1990-01-01T00:00:00.000Z",
"emails": ["john@example.com"],
"phones": ["+123456789"],
"verified": true,
"accountType": "individual"
}
}Internal applications use different methods to obtain and verify session information based on their domain.
- Same Domain (
neupgroup.com): Session information is automatically available via secure HTTP-only cookies (auth_account_id,auth_session_id,auth_session_key). - Different Domain: Internal apps must use the
POST https://neupgroup.com/account/bridge/api.v1/auth/signendpoint to obtain session details, similar to external apps.
Internal applications do not use REST endpoints for session verification. Instead, they use gRPC for maximum performance and low latency.
gRPC Service Definition:
The AuthService provides a VerifySession method:
service AuthService {
rpc VerifySession (VerifyRequest) returns (VerifyResponse);
}Internal apps under the Neup ecosystem now use two dedicated tables:
access_members: tracks whether a primary account has added another account as a member.account_access: tracks what role that member has inside a specific internal application and resource scope.
access_members
| Column | Meaning |
|---|---|
id |
Unique row ID |
parentOwnerId |
Primary account in the Neup auth system |
memberAccountId |
Account receiving delegated access |
status |
Usually invited or joined |
account_access
| Column | Meaning |
|---|---|
id |
Unique row ID |
accountId |
Account receiving access |
appId |
Internal application ID |
resourceId |
Resource scope inside the app. Use null in the API for app-wide access. |
parentOwnerId |
Owner of the app/resource in the Neup auth system |
role |
Granted role such as admin, editor, or viewer |
status |
Usually active or on_hold |
Internal apps use the same bridge endpoint, but the internal branch now reads and writes access_members and account_access.
- Endpoint:
POST https://neupgroup.com/account/bridge/api.v1/auth/access - Purpose: Create or update a membership row in
access_members.
Request Body
{
"aid": "PRIMARY_ACCOUNT_ID",
"sid": "EXTERNAL_SESSION_ID",
"skey": "EXTERNAL_SESSION_KEY",
"recipientId": "MEMBER_ACCOUNT_ID",
"parentOwnerId": "PRIMARY_ACCOUNT_ID",
"status": "joined"
}- Endpoint:
PATCH https://neupgroup.com/account/bridge/api.v1/auth/access - Purpose: Create or update a row in
account_access. - Rule: a matching
access_membersrow must already exist unless the primary account is granting access to itself.
Request Body
{
"aid": "PRIMARY_ACCOUNT_ID",
"sid": "EXTERNAL_SESSION_ID",
"skey": "EXTERNAL_SESSION_KEY",
"recipientId": "MEMBER_ACCOUNT_ID",
"parentOwnerId": "PRIMARY_ACCOUNT_ID",
"resourceId": "profile_15",
"role": "editor",
"status": "active"
}Notes:
resourceIdreplaces the olderaccess_toandassetIdnaming.parentOwnerIdreplaces the olderownerIdnaming for internal access.- If
resourceIdis omitted, the API stores app-wide access internally using a reserved value and returnsresourceId: nullin the response. - Internal apps should use
roleandstatusfor updates.addandremovearrays are for external-app permission records, not internal app access rows.
- Endpoint:
GET https://neupgroup.com/account/bridge/api.v1/auth/access
Response Body
{
"success": true,
"aid": "MEMBER_ACCOUNT_ID",
"appId": "neup_ads",
"isInternal": true,
"role": "editor",
"memberships": [
{
"parentOwnerId": "PRIMARY_ACCOUNT_ID",
"memberAccountId": "MEMBER_ACCOUNT_ID",
"status": "joined"
}
],
"accountAccess": [
{
"accountId": "MEMBER_ACCOUNT_ID",
"application": "neup_ads",
"resourceId": "profile_15",
"parentOwnerId": "PRIMARY_ACCOUNT_ID",
"role": "editor",
"status": "active"
}
],
"permissions": ["editor"]
}This model scales well for internal apps when the following indexed lookups remain intact:
- membership lookup by
parentOwnerId + memberAccountId - access lookup by
accountId + appId + status - access lookup by
parentOwnerId + appId + status - uniqueness on
accountId + appId + resourceId + parentOwnerId
When a user logs out from your application, notify Neup.Account to revoke the mapping.
Endpoint: POST https://neupgroup.com/account/bridge/api.v1/auth/signout
Request Body:
{
"appId": "YOUR_APP_ID",
"sessionValue": "YOUR_APP_SESSION_TOKEN"
}To prevent users from manually modifying their permissions in sessionStorage, Neup.Account uses an asymmetric cryptographic signing mechanism.
- RSA-2048 Key Pair:
- Private Key (Server-Side): Used to create digital signatures for permission sets.
- Public Key (Client-Side): Hardcoded in the frontend to verify signatures.
- Digital Signatures (RSA-SHA256): The server generates a cryptographic hash of the permissions and signs it with the Private Key, providing integrity and authenticity.
- Signing: The server fetches permissions, stringifies them, signs them with the Private Key, and returns a base64-encoded payload (
dataandsignature). - Verification: The
SessionProvideruses the pinned Public Key to verify the signature against the data usingwindow.crypto.subtle. - Protection: If tampering is detected, the application rejects the cached session and initiates a fresh request to the server.
All applications (Internal & External) must maintain a local copy of core user data to ensure resilience.
account_id: (Primary Key/Unique) The NeupaccountId.display_name: The user's chosen display name.display_image: URL to the user's profile photo.neup_id: The unique Neup identity string.
CREATE TABLE users (
account_id VARCHAR(255) PRIMARY KEY, -- Neup accountId
display_name VARCHAR(255),
display_image TEXT,
neup_id VARCHAR(100) UNIQUE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);- Server-to-Server Only: All calls to the bridge API (
/sign,/signout) must be performed from your server. - Session Cascading: If a user logs out of Neup.Account, all associated
sessionValuetokens are automatically revoked. - Token Storage: Store the
sessionValuesecurely (e.g., in an HTTP-only cookie). - Public Key Pinning: The application ignores stored keys in
sessionStorageand always uses the hardcoded public key for verification.
GET https://neupgroup.com/account/bridge/handshake.v1/auth/grant?appId=app_123&redirectsTo=https://yourapp.com/neup/callback
curl -X POST https://neupgroup.com/account/bridge/api.v1/auth/verify \
-H "Content-Type: application/json" \
-d '{
"appId": "app_123",
"appSecret": "secret_abc",
"key": "tempToken_value",
"accountId": "acct_456"
}'curl -X POST https://neupgroup.com/account/bridge/api.v1/auth/verify \
-H "Content-Type: application/json" \
-d '{
"appId": "app_123",
"appType": "internal",
"auth_account_id": "acct_456",
"auth_session_id": "sess_123",
"auth_session_key": "key_abc"
}'curl -X POST https://neupgroup.com/account/bridge/api.v1/auth/access \
-H "Content-Type: application/json" \
-d '{
"aid": "acct_primary",
"sid": "ext_sess_123",
"skey": "ext_key_abc",
"recipientId": "acct_member",
"parentOwnerId": "acct_primary",
"status": "joined"
}'curl -X PATCH https://neupgroup.com/account/bridge/api.v1/auth/access \
-H "Content-Type: application/json" \
-d '{
"aid": "acct_primary",
"sid": "ext_sess_123",
"skey": "ext_key_abc",
"recipientId": "acct_member",
"parentOwnerId": "acct_primary",
"resourceId": "profile_15",
"role": "editor",
"status": "active"
}'