> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nullpass.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Get/Update User Profile

> Get authenticated user profile or update profile information

## Endpoints

```
GET /api/auth/me
PATCH /api/auth/me
```

## GET /api/auth/me

Retrieves the authenticated user's profile including service access information.

### Request

Requires authentication via Bearer token.

### Response

<ResponseField name="user" type="object">
  User profile object

  <Expandable title="User Object">
    <ResponseField name="id" type="string">
      User ID
    </ResponseField>

    <ResponseField name="email" type="string">
      User email
    </ResponseField>

    <ResponseField name="displayName" type="string">
      Display name
    </ResponseField>

    <ResponseField name="avatar" type="string">
      Avatar URL or null
    </ResponseField>

    <ResponseField name="twoFactorEnabled" type="boolean">
      Whether 2FA is enabled
    </ResponseField>

    <ResponseField name="createdAt" type="string">
      Account creation timestamp
    </ResponseField>

    <ResponseField name="updatedAt" type="string">
      Last update timestamp
    </ResponseField>

    <ResponseField name="serviceAccess" type="array">
      Array of service entitlements
    </ResponseField>
  </Expandable>
</ResponseField>

## PATCH /api/auth/me

Updates user profile information. Only provided fields are updated.

### Request

<ParamField body="displayName" type="string">
  New display name (1-100 characters). Optional.
</ParamField>

<ParamField body="avatar" type="string">
  Avatar URL or empty string to remove avatar. Must be valid URL if provided. Optional.
</ParamField>

<Note>
  At least one field must be provided. Empty string for `avatar` removes the avatar.
</Note>

### Response

<ResponseField name="user" type="object">
  Updated user object (same structure as GET response)
</ResponseField>

## Implementation Details

### Code Reference

```15:109:nullpass_clean/src/app/api/auth/me/route.ts theme={null}
export async function GET(request: NextRequest) {
  const corsResponse = handleCors(request)
  if (corsResponse) return corsResponse

  const blocked = await protectRoute(request)
  if (blocked) return blocked

  const auth = await requireAuth(request)
  if ('error' in auth) return auth.error

  try {
    const user = await prisma.user.findUnique({
      where: { id: auth.userId },
      select: {
        id: true,
        email: true,
        avatar: true,
        displayName: true,
        twoFactorEnabled: true,
        createdAt: true,
        updatedAt: true,
        serviceAccess: true,
      },
    })

    if (!user) {
      return errorResponse('User not found', 404, request.headers.get('origin'))
    }

    return jsonResponse({ user }, 200, request.headers.get('origin'))
  } catch (error) {
    logger.error('Get user error:', error)
    return errorResponse('Internal server error', 500, request.headers.get('origin'))
  }
}

export async function PATCH(request: NextRequest) {
  const corsResponse = handleCors(request)
  if (corsResponse) return corsResponse

  const blocked = await protectRoute(request)
  if (blocked) return blocked

  const auth = await requireAuth(request)
  if ('error' in auth) return auth.error

  try {
    const body = await request.json()
    const validated = updateProfileSchema.parse(body)

    const updateData: {
      displayName?: string
      avatar?: string | null
    } = {}

    if (validated.displayName !== undefined) {
      updateData.displayName = validated.displayName
    }

    if (validated.avatar !== undefined) {
      updateData.avatar = validated.avatar === '' ? null : validated.avatar
    }

    if (Object.keys(updateData).length === 0) {
      return errorResponse('No fields to update', 400, request.headers.get('origin'))
    }

    const user = await prisma.user.update({
      where: { id: auth.userId },
      data: updateData,
      select: {
        id: true,
        email: true,
        avatar: true,
        displayName: true,
        twoFactorEnabled: true,
        createdAt: true,
        updatedAt: true,
      },
    })

    await createAuditLog(auth.userId, 'USER_UPDATE', {
      fields: Object.keys(updateData),
    })

    return jsonResponse({ user }, 200, request.headers.get('origin'))
  } catch (error: any) {
    if (error.name === 'ZodError') {
      logger.warn('Profile update validation error:', error.errors)
      return errorResponse(error.errors[0].message, 400, request.headers.get('origin'))
    }
    logger.error('Update profile error:', error)
    return errorResponse('Internal server error', 500, request.headers.get('origin'))
  }
}
```

## Status Codes

<ResponseField name="200" type="OK">
  Success
</ResponseField>

<ResponseField name="400" type="Bad Request">
  Validation error or no fields to update (PATCH only)
</ResponseField>

<ResponseField name="401" type="Unauthorized">
  Missing or invalid authentication token
</ResponseField>

<ResponseField name="404" type="Not Found">
  User not found (GET only)
</ResponseField>

## Example Requests

### GET Profile

```bash theme={null}
curl -X GET https://auth.nullpass.xyz/api/auth/me \
  -H "Authorization: Bearer YOUR_TOKEN"
```

### PATCH Profile

```bash theme={null}
curl -X PATCH https://auth.nullpass.xyz/api/auth/me \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "displayName": "Jane Doe",
    "avatar": "https://example.com/avatar.jpg"
  }'
```

### Remove Avatar

```bash theme={null}
curl -X PATCH https://auth.nullpass.xyz/api/auth/me \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "avatar": ""
  }'
```

## Audit Events

* **USER\_UPDATE**: Profile updated (includes list of updated fields)


## OpenAPI

````yaml GET /auth/me
openapi: 3.1.0
info:
  title: Null Pass API
  description: >-
    Internal API documentation for Null Pass authentication and service
    management system
  version: 1.0.0
servers:
  - url: https://auth.nullpass.xyz/api
security: []
paths:
  /auth/me:
    get:
      tags:
        - Authentication
      summary: Get User Profile
      description: Retrieve authenticated user's profile
      responses:
        '200':
          description: User profile
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserProfile'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      security:
        - bearerAuth: []
components:
  schemas:
    UserProfile:
      allOf:
        - $ref: '#/components/schemas/User'
        - type: object
          properties:
            serviceAccess:
              type: array
              items:
                $ref: '#/components/schemas/ServiceEntitlement'
    Error:
      type: object
      required:
        - error
        - message
      properties:
        error:
          type: string
        message:
          type: string
    User:
      type: object
      properties:
        id:
          type: string
        email:
          type: string
          format: email
        username:
          type: string
          nullable: true
        displayName:
          type: string
          nullable: true
        avatar:
          type: string
          nullable: true
        twoFactorEnabled:
          type: boolean
        createdAt:
          type: string
          format: date-time
    ServiceEntitlement:
      type: object
      properties:
        id:
          type: string
        userId:
          type: string
        service:
          type: string
          enum:
            - DROP
            - MAILS
            - VAULT
            - DB
        tier:
          type: string
          example: premium
        isPremium:
          type: boolean
        accessFlags:
          type: object
          additionalProperties: true
        metadata:
          type: object
          additionalProperties: true
        customStorageLimit:
          type: integer
          nullable: true
        customApiKeyLimit:
          type: integer
          nullable: true
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

````