Skip to main content
Retrieve session credentials to authenticate requests to your backend. When a user logs in to your app and becomes authenticated, MoonKey issues the user session credentials. These credentials allow your backend to verify that the requesting user is truly authenticated.

Session Credentials

MoonKey provides two types of session credentials:

Session Token

An opaque token that can be verified through the MoonKey API:
session_vy9YGpubKjVn98cw1nT25Msj7jaIpHBinUfD45KLdAOgn9NqEuE4qGHOEchEG5Ue
  • Must be verified via /sessions/verify API endpoint
  • Can be instantly revoked
  • No user information exposed in the token itself

Session JWT

A JSON Web Token (JWT) signed with RS256 that can be verified independently:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
  • Can be verified using public keys without API call
  • Contains session information in claims
  • Expires after 5 minutes but can be refreshed
Learn more about the differences in the Session Token vs JWT guide.

Getting Session Credentials

To retrieve the current user’s session credentials, use the getSessionTokens method from the useMoonKey hook:
getSessionTokens: () => Promise<{ 
  sessionToken: string; 
  sessionJwt: string 
} | null>

Import

import { useMoonKey } from '@moon-key/react-auth';

Basic Usage

import { useMoonKey } from '@moon-key/react-auth';

function MyComponent() {
  const { getSessionTokens } = useMoonKey();

  const fetchProtectedData = async () => {
    // Get session credentials
    const credentials = await getSessionTokens();
    
    if (!credentials) {
      console.error('User is not authenticated');
      return;
    }

    const { sessionToken, sessionJwt } = credentials;
    
    // Use credentials to authenticate backend request
    // (see examples below)
  };

  return (
    <button onClick={fetchProtectedData}>
      Fetch Data
    </button>
  );
}

Return Value

The getSessionTokens method returns:
  • An object with sessionToken and sessionJwt if the user is authenticated
  • null if the user is not authenticated
// When authenticated
{
  sessionToken: "session_abc123...",
  sessionJwt: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
}

// When not authenticated
null

Sending Session Credentials

When making requests to your backend, include the session credentials in the request headers.

With fetch

Using the session token (recommended for most use cases):
import { useMoonKey } from '@moon-key/react-auth';

export default function DataFetcher() {
  const { getSessionTokens } = useMoonKey();

  const fetchData = async () => {
    const credentials = await getSessionTokens();
    
    if (!credentials) {
      throw new Error('Not authenticated');
    }

    const response = await fetch('https://your-api.com/protected', {
      method: 'GET',
      headers: {
        'Authorization': `Bearer ${credentials.sessionToken}`,
        'Content-Type': 'application/json'
      }
    });

    return response.json();
  };

  return <button onClick={fetchData}>Load Data</button>;
}
Using the session JWT (for offline verification):
const credentials = await getSessionTokens();

if (!credentials) {
  throw new Error('Not authenticated');
}

const response = await fetch('https://your-api.com/protected', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${credentials.sessionJwt}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(data)
});

With axios

import axios from 'axios';
import { useMoonKey } from '@moon-key/react-auth';

export default function DataManager() {
  const { getSessionTokens } = useMoonKey();

  const postData = async (data: any) => {
    const credentials = await getSessionTokens();
    
    if (!credentials) {
      throw new Error('Not authenticated');
    }

    const response = await axios({
      method: 'post',
      url: 'https://your-api.com/protected',
      headers: {
        'Authorization': `Bearer ${credentials.sessionToken}`
      },
      data: data
    });

    return response.data;
  };

  return (
    <button onClick={() => postData({ foo: 'bar' })}>
      Submit
    </button>
  );
}

With custom hooks

Create a reusable hook for authenticated requests:
import { useMoonKey } from '@moon-key/react-auth';
import { useCallback } from 'react';

export function useAuthenticatedFetch() {
  const { getSessionTokens, isAuthenticated } = useMoonKey();

  const authenticatedFetch = useCallback(
    async (url: string, options: RequestInit = {}) => {
      if (!isAuthenticated) {
        throw new Error('User is not authenticated');
      }

      const credentials = await getSessionTokens();
      
      if (!credentials) {
        throw new Error('Failed to get session credentials');
      }

      const response = await fetch(url, {
        ...options,
        headers: {
          ...options.headers,
          'Authorization': `Bearer ${credentials.sessionToken}`,
          'Content-Type': 'application/json'
        }
      });

      if (response.status === 401) {
        throw new Error('Session expired');
      }

      return response;
    },
    [getSessionTokens, isAuthenticated]
  );

  return authenticatedFetch;
}

// Usage
function MyComponent() {
  const authenticatedFetch = useAuthenticatedFetch();

  const loadData = async () => {
    try {
      const response = await authenticatedFetch('https://your-api.com/data');
      const data = await response.json();
      console.log(data);
    } catch (error) {
      console.error('Failed to fetch:', error);
    }
  };

  return <button onClick={loadData}>Load Data</button>;
}

Common Patterns

API Client Wrapper

Create an API client that automatically includes session credentials:
import { useMoonKey } from '@moon-key/react-auth';

class APIClient {
  constructor(private getSessionTokens: () => Promise<any>) {}

  async get(endpoint: string) {
    return this.request('GET', endpoint);
  }

  async post(endpoint: string, data: any) {
    return this.request('POST', endpoint, data);
  }

  async request(method: string, endpoint: string, data?: any) {
    const credentials = await this.getSessionTokens();
    
    if (!credentials) {
      throw new Error('Not authenticated');
    }

    const response = await fetch(`https://your-api.com${endpoint}`, {
      method,
      headers: {
        'Authorization': `Bearer ${credentials.sessionToken}`,
        'Content-Type': 'application/json'
      },
      body: data ? JSON.stringify(data) : undefined
    });

    if (!response.ok) {
      throw new Error(`API error: ${response.status}`);
    }

    return response.json();
  }
}

// Usage in component
export default function DataComponent() {
  const { getSessionTokens } = useMoonKey();
  const api = new APIClient(getSessionTokens);

  const fetchUser = async () => {
    const user = await api.get('/user');
    console.log(user);
  };

  const updateUser = async (userData: any) => {
    const result = await api.post('/user/update', userData);
    console.log(result);
  };

  return (
    <div>
      <button onClick={fetchUser}>Get User</button>
      <button onClick={() => updateUser({ name: 'John' })}>
        Update User
      </button>
    </div>
  );
}

With React Query

Integrate with React Query for data fetching:
import { useQuery, useMutation } from '@tanstack/react-query';
import { useMoonKey } from '@moon-key/react-auth';

function useAuthenticatedQuery() {
  const { getSessionTokens } = useMoonKey();

  return useQuery({
    queryKey: ['userData'],
    queryFn: async () => {
      const credentials = await getSessionTokens();
      
      if (!credentials) {
        throw new Error('Not authenticated');
      }

      const response = await fetch('https://your-api.com/user', {
        headers: {
          'Authorization': `Bearer ${credentials.sessionToken}`
        }
      });

      if (!response.ok) {
        throw new Error('Failed to fetch user data');
      }

      return response.json();
    }
  });
}

// Usage
export default function UserProfile() {
  const { data, isLoading, error } = useAuthenticatedQuery();

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <div>
      <h1>User Profile</h1>
      <p>{data.email}</p>
    </div>
  );
}

Error Handling

Properly handle authentication errors:
import { useMoonKey } from '@moon-key/react-auth';
import { useState } from 'react';

export default function SecureDataFetcher() {
  const { getSessionTokens, start } = useMoonKey();
  const [error, setError] = useState<string | null>(null);

  const fetchSecureData = async () => {
    setError(null);
    
    try {
      const credentials = await getSessionTokens();
      
      if (!credentials) {
        setError('Please log in to continue');
        return;
      }

      const response = await fetch('https://your-api.com/secure-data', {
        headers: {
          'Authorization': `Bearer ${credentials.sessionToken}`
        }
      });

      if (response.status === 401) {
        setError('Your session has expired. Please log in again.');
        return;
      }

      if (!response.ok) {
        throw new Error(`Server error: ${response.status}`);
      }

      const data = await response.json();
      console.log('Fetched data:', data);
      
    } catch (err) {
      setError(err instanceof Error ? err.message : 'An error occurred');
    }
  };

  return (
    <div>
      <button onClick={fetchSecureData}>Fetch Secure Data</button>
      {error && (
        <div style={{ color: 'red' }}>
          {error}
          {error.includes('log in') && (
            <button onClick={() => start()}>Login</button>
          )}
        </div>
      )}
    </div>
  );
}

With Next.js API Routes

Make authenticated requests to Next.js API routes:
// Client component
import { useMoonKey } from '@moon-key/react-auth';

export default function ClientComponent() {
  const { getSessionTokens } = useMoonKey();

  const callAPI = async () => {
    const credentials = await getSessionTokens();
    
    if (!credentials) {
      console.error('Not authenticated');
      return;
    }

    const response = await fetch('/api/protected', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${credentials.sessionToken}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ data: 'example' })
    });

    const result = await response.json();
    console.log(result);
  };

  return <button onClick={callAPI}>Call Protected API</button>;
}
// app/api/protected/route.ts (Next.js App Router)
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const authHeader = request.headers.get('authorization');
  
  if (!authHeader?.startsWith('Bearer ')) {
    return NextResponse.json(
      { error: 'Missing authorization header' },
      { status: 401 }
    );
  }

  const sessionToken = authHeader.substring(7);

  // Verify session with MoonKey API
  const verifyResponse = await fetch(
    'https://api.moonkey.fun/v1/auth/sessions/verify',
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.MOONKEY_SECRET_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ session_token: sessionToken })
    }
  );

  if (!verifyResponse.ok) {
    return NextResponse.json(
      { error: 'Invalid session' },
      { status: 401 }
    );
  }

  const { user } = await verifyResponse.json();

  // Process request with authenticated user
  return NextResponse.json({
    message: 'Success',
    userId: user.id
  });
}

Best Practices

Always Check for Null

The getSessionTokens method returns null when the user is not authenticated:
const credentials = await getSessionTokens();

if (!credentials) {
  // Handle unauthenticated state
  console.error('User is not authenticated');
  return;
}

// Safe to use credentials here
const { sessionToken, sessionJwt } = credentials;

Handle Expired Sessions

Session tokens can expire or become invalid:
const fetchData = async () => {
  const credentials = await getSessionTokens();
  
  if (!credentials) {
    throw new Error('Not authenticated');
  }

  const response = await fetch('https://your-api.com/data', {
    headers: {
      'Authorization': `Bearer ${credentials.sessionToken}`
    }
  });

  if (response.status === 401) {
    // Session expired - redirect to login or refresh
    console.error('Session expired');
    // Optionally trigger re-authentication
    start();
    return;
  }

  return response.json();
};

Choose the Right Credential Type

Use Session Token when:
  • You need real-time revocation
  • Security is the top priority
  • You’re making requests to your own backend
Use Session JWT when:
  • You need offline verification
  • Performance is critical (no API call needed)
  • You’re integrating with external JWT-based systems

Secure Storage

The MoonKey SDK automatically handles secure storage of session credentials in IndexedDB. You don’t need to manually store these values.