Skip to content

Latest commit

 

History

History
1552 lines (1140 loc) · 19.6 KB

File metadata and controls

1552 lines (1140 loc) · 19.6 KB

API Reference

Complete reference for all Neev API endpoints. All API routes are prefixed with the configurable route prefix — route_prefix in config/neev.php (env NEEV_ROUTE_PREFIX), default neev. This documentation uses the default /neev prefix throughout.


Authentication

All authenticated endpoints require a Bearer token in the Authorization header:

Authorization: Bearer {token_id}|{token}

The header is the only accepted transport — query-string and request-body tokens were removed in v0.4.4 (tokens in URLs leak via logs, referrers, and browser history).

SPA cookie mode: same-origin SPAs whose host is listed in config('neev.spa.stateful') may instead carry the token in an HttpOnly cookie — the EnsureSpaRequestsAreStateful middleware promotes it to the Authorization header. State-changing requests (POST/PUT/PATCH/DELETE) from stateful origins must echo the CSRF cookie in the X-XSRF-TOKEN header or they are rejected with 419. See SPA Cookie Mode.


Authentication Endpoints

CSRF Cookie (SPA cookie mode)

Issues the signed double-submit CSRF cookie. SPAs call this once on app load (and again after a 419).

GET /neev/csrf-cookie

Response: 204 No Content with an XSRF-TOKEN cookie (not HttpOnly — the SPA reads it and echoes the value in X-XSRF-TOKEN). Throttled to 60 requests/minute.


Register

Create a new user account.

POST /neev/register

Request Body:

{
    "name": "John Doe",
    "email": "john@example.com",
    "password": "SecurePass123!",
    "password_confirmation": "SecurePass123!",
    "username": "johndoe"  // Optional, if support_username enabled
}

Response:

{
    "auth_state": "authenticated",
    "token": "1|abc123...",
    "expires_in": 1440,
    "mfa_options": null,
    "email_verified": false
}

Login

Authenticate with email/username and password.

POST /neev/login

Request Body:

{
    "email": "john@example.com",
    "password": "SecurePass123!"
}

Response (without MFA):

{
    "auth_state": "authenticated",
    "token": "1|abc123...",
    "expires_in": 1440,
    "mfa_options": null,
    "email_verified": true
}

Response (with MFA):

{
    "auth_state": "mfa_required",
    "token": "jwt_mfa_token...",
    "expires_in": 30,
    "mfa_options": [
        "authenticator",
        "email"
    ],
    "email_verified": true
}

When auth_state is mfa_required, the token is a short-lived JWT (type mfa) that can only be used to verify MFA. Complete MFA verification to get a full access token. expires_in is returned in minutes.


Send Login Link

Send a magic link to the user's email for passwordless login.

POST /neev/sendLoginLink

Request Body:

{
    "email": "john@example.com"
}

Response:

{
    "message": "Login link has been sent."
}

Login Using Link

Authenticate using a magic link.

GET /neev/loginUsingLink?id={email_id}&signature={signature}&expires={timestamp}

Response:

{
    "auth_state": "authenticated",
    "token": "1|abc123...",
    "expires_in": 1440,
    "email_verified": true
}

Logout

Logout the current session.

POST /neev/logout

Headers:

Authorization: Bearer {token}

Response:

{
    "message": "Logged out successfully."
}

Logout All Sessions

Logout from all other devices — the current session survives.

POST /neev/logoutAll

Headers:

Authorization: Bearer {token}

Response:

{
    "message": "Logged out from all other devices successfully."
}

Forgot Password

Send a signed URL password reset link to the user's email.

POST /neev/forgotPassword

Request Body:

{
    "email": "john@example.com"
}

Response:

{
    "message": "Password reset link has been sent to your email."
}

Reset Password

Reset the user's password using a signed URL from the forgot password email. The frontend receives the signed URL parameters and forwards them to this endpoint.

POST /neev/resetPassword?id={user_id}&hash={email_hash}&signature={signature}&expires={timestamp}

Request Body:

{
    "password": "NewSecurePass123!",
    "password_confirmation": "NewSecurePass123!"
}

Response:

{
    "message": "Password has been updated."
}

Email Verification

Send Verification Email

Resend the verification email to the authenticated user's current email address.

POST /neev/email/send

Headers:

Authorization: Bearer {token}

Response:

{
    "message": "Verification link has been sent."
}

Verify Email

GET /neev/email/verify?id={user_id}&hash={email_hash}&signature={signature}&expires={timestamp}

Headers:

Authorization: Bearer {token}

Response:

{
    "message": "Email verification done."
}

Email Change

Request Email Change

Request to change the authenticated user's email address. Sends a verification link to the new email. Requires current password for security.

POST /neev/email/change

Headers:

Authorization: Bearer {token}

Request Body:

{
    "email": "newemail@example.com",
    "password": "CurrentPass123!"
}

Response:

{
    "message": "Verification link has been sent to your new email address."
}

Verify Email Change

Verify the email change using the signed URL sent to the new email address. The frontend receives the signed URL parameters and forwards them to this endpoint.

POST /neev/email/change/verify?id={user_id}&email={new_email}&signature={signature}&expires={timestamp}

Response (success):

{
    "message": "Email address has been updated and verified."
}

Response (email already taken):

{
    "message": "This email address is already in use."
}

Multi-Factor Authentication

Add MFA Method

POST /neev/mfa/add

Headers:

Authorization: Bearer {token}

Request Body:

{
    "auth_method": "authenticator"  // or "email"
}

Response (authenticator):

{
    "qr_code": "<svg>...</svg>",
    "secret": "JBSWY3DPEHPK3PXP",
    "method": "authenticator"
}

The authenticator method is created in a pending state and is not enforced at login until activated via Verify MFA Setup. The email method is created active immediately.

Response (email):

{
    "message": "Email Configured."
}

Verify MFA Setup

Activate a pending authenticator setup by verifying a TOTP code. On success the method becomes active (and preferred, if no other active method is preferred).

POST /neev/mfa/setup/verify

Headers:

Authorization: Bearer {token}

Request Body:

{
    "auth_method": "authenticator",
    "otp": "123456"
}

Response:

{
    "message": "Method has been verified and enabled.",
    "method": "authenticator"
}

Response (wrong code or no pending setup, 400):

{
    "message": "Code verification failed."
}

Verify MFA OTP

Complete MFA verification after login.

POST /neev/mfa/otp/verify

Headers:

Authorization: Bearer {mfa_jwt_token}

Request Body:

{
    "auth_method": "authenticator",
    "otp": "123456"
}

Response:

{
    "auth_state": "authenticated",
    "token": "1|abc123...",
    "expires_in": 1440,
    "email_verified": true
}

Delete MFA Method

DELETE /neev/mfa/delete

Headers:

Authorization: Bearer {token}

Request Body:

{
    "auth_method": "authenticator"
}

Response:

{
    "message": "Auth has been deleted."
}

Generate Recovery Codes

POST /neev/recoveryCodes

Headers:

Authorization: Bearer {token}

Response:

{
    "message": "New recovery codes are generated.",
    "data": [
        "abc123defg",
        "hij456klmn",
        "opq789rstu"
    ]
}

Passkeys (WebAuthn)

Generate Registration Options

GET /neev/passkeys/register/options

Headers:

Authorization: Bearer {token}

Response:

{
    "rp": {
        "name": "Your App",
        "id": "yourapp.com"
    },
    "user": {
        "id": "base64_user_id",
        "name": "john@example.com",
        "displayName": "John Doe"
    },
    "challenge": "base64_challenge",
    "pubKeyCredParams": [...],
    "authenticatorSelection": {
        "residentKey": "required",
        "userVerification": "required"
    },
    "timeout": 60000,
    "attestation": "none"
}

Register Passkey

POST /neev/passkeys/register

Headers:

Authorization: Bearer {token}

Request Body:

{
    "attestation": "{...attestation_response_json...}",
    "name": "My MacBook"
}

Response:

{
    "message": "Passkey has been registered.",
    "data": {
        "id": 1,
        "name": "My MacBook",
        "created_at": "2024-01-15T10:00:00Z"
    }
}

Generate Login Options

GET /neev/passkeys/login/options?email=john@example.com

Response:

{
    "challenge": "base64_challenge",
    "timeout": 120000,
    "rpId": "yourapp.com",
    "allowCredentials": [...],
    "userVerification": "required"
}

Login with Passkey

POST /neev/passkeys/login

Request Body:

{
    "email": "john@example.com",
    "assertion": "{...assertion_response_json...}"
}

Response:

{
    "auth_state": "authenticated",
    "token": "1|abc123...",
    "expires_in": 1440,
    "email_verified": true
}

Update Passkey Name

PUT /neev/passkeys

Headers:

Authorization: Bearer {token}

Request Body:

{
    "passkey_id": 1,
    "name": "Work Laptop"
}

Delete Passkey

DELETE /neev/passkeys

Headers:

Authorization: Bearer {token}

Request Body:

{
    "passkey_id": 1
}

User Management

Get Current User

GET /neev/users

Headers:

Authorization: Bearer {token}

Response:

{
    "data": {
        "id": 1,
        "name": "John Doe",
        "username": "johndoe",
        "active": true,
        "emails": [...],
        "teams": [...]
    }
}

Update User

PUT /neev/users

Headers:

Authorization: Bearer {token}

Request Body:

{
    "name": "John Smith",
    "username": "johnsmith"
}

Response:

{
    "message": "Account has been updated.",
    "data": {...}
}

Delete User

DELETE /neev/users

Headers:

Authorization: Bearer {token}

Request Body:

{
    "password": "CurrentPassword123!"
}

Response:

{
    "message": "Account has been deleted."
}

Change Password

PUT /neev/changePassword

Headers:

Authorization: Bearer {token}

Request Body:

{
    "current_password": "OldPassword123!",
    "password": "NewPassword456!",
    "password_confirmation": "NewPassword456!"
}

Response:

{
    "message": "Password has been successfully updated."
}

Sessions & Login History

Get Active Sessions

GET /neev/sessions

Headers:

Authorization: Bearer {token}

Response:

{
    "data": [
        {
            "id": 1,
            "name": "login",
            "last_used_at": "2024-01-15T10:00:00Z",
            "attempt": {
                "ip_address": "192.168.1.1",
                "browser": "Chrome",
                "platform": "macOS",
                "location": "San Francisco, CA, US"
            }
        }
    ]
}

Revoke a Session

DELETE /neev/sessions/{id}

Deletes one of the user's login sessions (the underlying login token), immediately invalidating it. The current session cannot be revoked this way — use POST /neev/logout instead.

Headers:

Authorization: Bearer {token}

Response:

{
    "message": "Session has been revoked."
}

Errors:

Status Condition
400 {id} is the current session
404 Session does not exist or belongs to another user

Get Login Attempts

GET /neev/loginAttempts

Headers:

Authorization: Bearer {token}

Response:

{
    "data": [
        {
            "id": 1,
            "method": "password",
            "multi_factor_method": "authenticator",
            "ip_address": "192.168.1.1",
            "browser": "Chrome",
            "platform": "macOS",
            "device": "Desktop",
            "location": "San Francisco, CA, US",
            "is_success": true,
            "is_suspicious": false,
            "created_at": "2024-01-15T10:00:00Z"
        }
    ]
}

API Tokens

Get API Tokens

GET /neev/apiTokens

Headers:

Authorization: Bearer {token}

Response:

{
    "data": [
        {
            "id": 1,
            "name": "Mobile App",
            "permissions": ["read", "write"],
            "last_used_at": "2024-01-15T10:00:00Z",
            "expires_at": null,
            "created_at": "2024-01-10T10:00:00Z"
        }
    ]
}

Create API Token

POST /neev/apiTokens

Headers:

Authorization: Bearer {token}

Request Body:

{
    "name": "Mobile App",
    "permissions": ["read", "write"],
    "expiry": 43200  // minutes (30 days), null for no expiry
}

Response:

{
    "message": "Token has been added.",
    "data": {
        "accessToken": {...},
        "plainTextToken": "1|abc123..."
    }
}

Update API Token

PUT /neev/apiTokens

Headers:

Authorization: Bearer {token}

Request Body:

{
    "token_id": 1,
    "name": "Updated Name",
    "permissions": ["read"],
    "expiry": 10080  // new expiry in minutes
}

Delete API Token

DELETE /neev/apiTokens

Headers:

Authorization: Bearer {token}

Request Body:

{
    "token_id": 1
}

Delete All API Tokens

DELETE /neev/apiTokens/deleteAll

Headers:

Authorization: Bearer {token}

Team Management

Get User's Teams

GET /neev/teams

Headers:

Authorization: Bearer {token}

Response:

{
    "data": [
        {
            "id": 1,
            "name": "My Team",
            "slug": "my-team",
            "is_public": false,
            "owner": {...},
            "membership": {
                "role": "admin",
                "joined": true
            }
        }
    ]
}

Set Default Team

Set the user's default team (the team to land on after login).

PUT /neev/teams/default

Headers:

Authorization: Bearer {token}

Request Body:

{
    "team_id": 1
}

Response:

{
    "message": "Default team updated successfully.",
    "data": {...}
}

Get Team Details

GET /neev/teams/{id}

Headers:

Authorization: Bearer {token}

Response:

{
    "data": {
        "id": 1,
        "name": "My Team",
        "slug": "my-team",
        "is_public": false,
        "owner": {...},
        "users": [...],
        "joinRequests": [...],
        "invitedUsers": [...],
        "invitations": [...]
    }
}

Create Team

POST /neev/teams

Headers:

Authorization: Bearer {token}

Request Body:

{
    "name": "New Team",
    "public": false
}

Update Team

PUT /neev/teams

Headers:

Authorization: Bearer {token}

Request Body:

{
    "team_id": 1,
    "name": "Updated Team Name",
    "public": true
}

Delete Team

DELETE /neev/teams

Headers:

Authorization: Bearer {token}

Request Body:

{
    "team_id": 1
}

Invite Team Member

POST /neev/teams/inviteUser

Headers:

Authorization: Bearer {token}

Request Body:

{
    "team_id": 1,
    "email": "newmember@example.com",
    "role": "member"
}

Accept/Reject Invitation

PUT /neev/teams/inviteUser

Headers:

Authorization: Bearer {token}

Request Body:

{
    "team_id": 1,
    "action": "accept"  // or "reject"
}

Or for email invitations:

{
    "invitation_id": 1,
    "action": "accept"
}

Leave Team

PUT /neev/teams/leave

Headers:

Authorization: Bearer {token}

Request Body:

{
    "team_id": 1
}

Request to Join Team

POST /neev/teams/request

Headers:

Authorization: Bearer {token}

Request Body:

{
    "team_id": 1
}

Accept/Reject Join Request

PUT /neev/teams/request

Headers:

Authorization: Bearer {token}

Request Body:

{
    "team_id": 1,
    "user_id": 5,
    "action": "accept",
    "role": "member"
}

Change Team Owner

POST /neev/changeTeamOwner

Headers:

Authorization: Bearer {token}

Request Body:

{
    "team_id": 1,
    "user_id": 5
}

Change Member Role

PUT /neev/role/change

Headers:

Authorization: Bearer {token}

Request Body:

{
    "team_id": 1,
    "user_id": 5,
    "role": "admin"
}

Domain Federation

Get Team Domains

GET /neev/domains?team_id=1

Headers:

Authorization: Bearer {token}

Add Domain

POST /neev/domains

Headers:

Authorization: Bearer {token}

Request Body:

{
    "team_id": 1,
    "domain": "company.com",
    "enforce": false
}

Response:

{
    "message": "Domain federated successfully.",
    "token": "abc123verification..."
}

Verify Domain

PUT /neev/domains

Headers:

Authorization: Bearer {token}

Request Body:

{
    "domain_id": 1,
    "verify": true
}

Delete Domain

DELETE /neev/domains

Headers:

Authorization: Bearer {token}

Request Body:

{
    "domain_id": 1
}

Tenant Domains (Multi-Tenancy)

Get Tenant Domains

GET /neev/tenant-domains

Headers:

Authorization: Bearer {token}

Add Tenant Domain

POST /neev/tenant-domains

Headers:

Authorization: Bearer {token}

Request Body:

{
    "domain": "custom.example.com"
}

Verify Tenant Domain

POST /neev/tenant-domains/{id}/verify

Set Primary Tenant Domain

POST /neev/tenant-domains/{id}/primary

Error Responses

All endpoints return consistent error responses:

{
    "message": "Error description here."
}

Common HTTP Status Codes:

Code Description
200 Success
400 Bad Request (validation error)
401 Unauthorized (invalid/missing token)
403 Forbidden (insufficient permissions)
404 Not Found
500 Server Error

Next Steps