API Authentication

MyAppAPI supports multiple authentication methods to meet the needs of different integration scenarios. This guide explains how to authenticate your requests using API keys, OAuth 2.0, and JWT tokens, along with best practices for secure implementation.

Authentication Overview

All requests to the MyAppAPI require authentication. Unauthenticated requests will return a 401 Unauthorized error. We offer three primary authentication methods to support different use cases:

Authentication Method Best For Security Level Implementation Complexity
API Keys Server-to-server integrations, internal applications Medium Low
OAuth 2.0 User-facing applications, third-party integrations High Medium-High
JWT Tokens Mobile apps, SPAs, microservices High Medium

Regardless of the authentication method you choose, all API requests should be made over HTTPS to ensure secure transmission of credentials. Requests made over plain HTTP will be rejected.

API Key Authentication

API keys provide a simple way to authenticate requests to our API. They are best suited for server-to-server integrations where the key can be stored securely.

Obtaining an API Key

To obtain an API key:

  1. Log in to your MyAppAPI Dashboard.
  2. Navigate to the API Keys section.
  3. Click Create API Key and provide a descriptive name for the key.
  4. Copy and securely store your API key. For security reasons, we only show the complete key once upon creation.

Important: Keep your API key secure! Never expose it in client-side code or commit it to public repositories. Treat it like a password.

Using API Keys

There are two ways to include your API key in requests:

1. Authorization Header (Recommended)

Include your API key in the Authorization header with the Bearer prefix:

GET /v1/resources HTTP/1.1
Host: api.myappapi.com
Authorization: Bearer YOUR_API_KEY

2. Query Parameter

You can also pass your API key as a query parameter. This method is less secure and should be used only when you cannot set HTTP headers:

GET /v1/resources?api_key=YOUR_API_KEY HTTP/1.1
Host: api.myappapi.com

API Key Best Practices

  • Use environment variables to store API keys in your application.
  • Implement proper API key rotation practices.
  • Create separate API keys for different environments (development, staging, production).
  • Set appropriate permissions for each API key to follow the principle of least privilege.
  • Use dedicated keys for different integrations to simplify tracking and revocation if needed.

API Key Examples

curl -X GET "https://api.myappapi.com/v1/resources" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json"
// Using fetch
const apiKey = process.env.MY_APP_API_KEY; // Store keys securely

fetch('https://api.myappapi.com/v1/resources', {
  method: 'GET',
  headers: {
    'Authorization': `Bearer ${apiKey}`,
    'Content-Type': 'application/json'
  }
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
import requests
import os

# Store keys securely
api_key = os.environ.get('MY_APP_API_KEY')

headers = {
    'Authorization': f'Bearer {api_key}',
    'Content-Type': 'application/json'
}

response = requests.get(
    'https://api.myappapi.com/v1/resources',
    headers=headers
)

if response.status_code == 200:
    data = response.json()
    print(data)
else:
    print(f"Request failed with status code: {response.status_code}")
require 'net/http'
require 'uri'
require 'json'

# Store keys securely
api_key = ENV['MY_APP_API_KEY']

uri = URI('https://api.myappapi.com/v1/resources')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Get.new(uri)
request['Authorization'] = "Bearer #{api_key}"
request['Content-Type'] = 'application/json'

response = http.request(request)

if response.code.to_i == 200
  data = JSON.parse(response.body)
  puts data
else
  puts "Request failed with status code: #{response.code}"
end
<?php
// Store keys securely
$apiKey = getenv('MY_APP_API_KEY');

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.myappapi.com/v1/resources');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer ' . $apiKey,
    'Content-Type: application/json'
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

curl_close($ch);

if ($httpCode == 200) {
    $data = json_decode($response, true);
    print_r($data);
} else {
    echo "Request failed with status code: " . $httpCode;
}
?>

OAuth 2.0 Authentication

OAuth 2.0 is an authorization framework that enables third-party applications to obtain limited access to a user's account. It's ideal for applications that need to access data on behalf of users without handling their credentials.

OAuth 2.0 Flow Overview

MyAppAPI supports the following OAuth 2.0 flows:

  • Authorization Code Flow - For server-side applications
  • Authorization Code Flow with PKCE - For mobile and single-page applications
  • Client Credentials Flow - For service-to-service authentication

Setting Up OAuth 2.0

To use OAuth 2.0 with MyAppAPI:

  1. Log in to your MyAppAPI Dashboard.
  2. Navigate to the OAuth Applications section.
  3. Click Create Application and provide the required information:
    • Application name
    • Redirect URI(s)
    • Allowed scopes
    • Application type
  4. After creating the application, you'll receive a Client ID and Client Secret.

Note: Store your Client Secret securely and never expose it in client-side code.

Authorization Code Flow with PKCE

For mobile and single-page applications, we recommend using the Authorization Code Flow with PKCE (Proof Key for Code Exchange). This adds an extra layer of security by preventing authorization code interception attacks.

Step 1: Generate a Code Verifier and Challenge

// JavaScript example of code verifier and challenge generation
function generateCodeVerifier() {
  const array = new Uint8Array(32);
  window.crypto.getRandomValues(array);
  return base64URLEncode(array);
}

function base64URLEncode(buffer) {
  return btoa(String.fromCharCode.apply(null, new Uint8Array(buffer)))
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=+$/, '');
}

async function generateCodeChallenge(verifier) {
  const encoder = new TextEncoder();
  const data = encoder.encode(verifier);
  const digest = await window.crypto.subtle.digest('SHA-256', data);
  return base64URLEncode(digest);
}

// Generate and store code verifier
const codeVerifier = generateCodeVerifier();
localStorage.setItem('code_verifier', codeVerifier);

// Generate code challenge
generateCodeChallenge(codeVerifier).then(codeChallenge => {
  // Use code challenge in authorization request
  console.log(codeChallenge);
});

Step 2: Redirect User to Authorization URL

// Redirect the user to the authorization URL
function redirectToAuthUrl(codeChallenge) {
  const authUrl = new URL('https://auth.myappapi.com/oauth/authorize');
  authUrl.searchParams.append('client_id', 'YOUR_CLIENT_ID');
  authUrl.searchParams.append('redirect_uri', 'https://your-app.com/callback');
  authUrl.searchParams.append('response_type', 'code');
  authUrl.searchParams.append('scope', 'read write');
  authUrl.searchParams.append('code_challenge', codeChallenge);
  authUrl.searchParams.append('code_challenge_method', 'S256');
  authUrl.searchParams.append('state', generateRandomState());
  
  window.location = authUrl.toString();
}

function generateRandomState() {
  const array = new Uint8Array(16);
  window.crypto.getRandomValues(array);
  return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
}

Step 3: Exchange Authorization Code for Tokens

// Handle the callback and exchange code for tokens
async function handleAuthCallback() {
  const urlParams = new URLSearchParams(window.location.search);
  const code = urlParams.get('code');
  const state = urlParams.get('state');
  
  // Verify state matches original state sent
  if (state !== storedState) {
    throw new Error('State mismatch - possible CSRF attack');
  }
  
  // Retrieve the code verifier from storage
  const codeVerifier = localStorage.getItem('code_verifier');
  
  const tokenResponse = await fetch('https://auth.myappapi.com/oauth/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: new URLSearchParams({
      grant_type: 'authorization_code',
      client_id: 'YOUR_CLIENT_ID',
      code_verifier: codeVerifier,
      code: code,
      redirect_uri: 'https://your-app.com/callback'
    })
  });
  
  if (!tokenResponse.ok) {
    throw new Error('Failed to exchange authorization code for tokens');
  }
  
  const tokens = await tokenResponse.json();
  
  // Store tokens securely
  localStorage.setItem('access_token', tokens.access_token);
  localStorage.setItem('refresh_token', tokens.refresh_token);
  localStorage.setItem('expires_at', Date.now() + tokens.expires_in * 1000);
  
  return tokens;
}

Step 4: Use Access Token for API Requests

// Use the access token to make API requests
async function makeApiRequest(endpoint) {
  // Check if token has expired
  const expiresAt = localStorage.getItem('expires_at');
  if (Date.now() > expiresAt) {
    await refreshAccessToken();
  }
  
  const accessToken = localStorage.getItem('access_token');
  
  const response = await fetch(`https://api.myappapi.com/v1/${endpoint}`, {
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'Content-Type': 'application/json'
    }
  });
  
  return response.json();
}

// Refresh access token using refresh token
async function refreshAccessToken() {
  const refreshToken = localStorage.getItem('refresh_token');
  
  const response = await fetch('https://auth.myappapi.com/oauth/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: new URLSearchParams({
      grant_type: 'refresh_token',
      client_id: 'YOUR_CLIENT_ID',
      refresh_token: refreshToken
    })
  });
  
  if (!response.ok) {
    // If refresh fails, redirect to login
    window.location = '/login';
    return;
  }
  
  const tokens = await response.json();
  
  localStorage.setItem('access_token', tokens.access_token);
  localStorage.setItem('refresh_token', tokens.refresh_token || refreshToken);
  localStorage.setItem('expires_at', Date.now() + tokens.expires_in * 1000);
}

Authorization Code Flow (for Web Apps)

For server-side web applications, you can use the standard Authorization Code Flow. This flow is similar to the PKCE flow but doesn't require code challenges. Instead, it uses a client secret that must be kept securely on your server.

Note: The client secret should never be exposed to the client side. Keep it secure on your server.

Example Server-Side Implementation (Node.js/Express)

const express = require('express');
const axios = require('axios');
const session = require('express-session');
const crypto = require('crypto');
const app = express();

app.use(session({
  secret: 'your-session-secret',
  resave: false,
  saveUninitialized: true
}));

// Config
const config = {
  client_id: 'YOUR_CLIENT_ID',
  client_secret: 'YOUR_CLIENT_SECRET',
  redirect_uri: 'http://localhost:3000/callback',
  auth_url: 'https://auth.myappapi.com/oauth/authorize',
  token_url: 'https://auth.myappapi.com/oauth/token',
  api_url: 'https://api.myappapi.com/v1'
};

app.get('/login', (req, res) => {
  // Generate and store state
  const state = crypto.randomBytes(16).toString('hex');
  req.session.oauth_state = state;
  
  // Redirect to authorization endpoint
  const authUrl = new URL(config.auth_url);
  authUrl.searchParams.append('client_id', config.client_id);
  authUrl.searchParams.append('redirect_uri', config.redirect_uri);
  authUrl.searchParams.append('response_type', 'code');
  authUrl.searchParams.append('scope', 'read write');
  authUrl.searchParams.append('state', state);
  
  res.redirect(authUrl.toString());
});

app.get('/callback', async (req, res) => {
  const { code, state } = req.query;
  
  // Verify state
  if (state !== req.session.oauth_state) {
    return res.status(403).send('State mismatch - possible CSRF attack');
  }
  
  try {
    // Exchange code for tokens
    const tokenResponse = await axios.post(config.token_url, 
      new URLSearchParams({
        grant_type: 'authorization_code',
        client_id: config.client_id,
        client_secret: config.client_secret,
        code: code,
        redirect_uri: config.redirect_uri
      }).toString(),
      {
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded'
        }
      }
    );
    
    // Store tokens in session (or better, in a secure token store)
    req.session.tokens = tokenResponse.data;
    req.session.tokens.expires_at = Date.now() + tokenResponse.data.expires_in * 1000;
    
    res.redirect('/dashboard');
  } catch (error) {
    res.status(500).send('Error obtaining access token');
  }
});

// API request middleware to check token expiration
async function checkToken(req, res, next) {
  if (!req.session.tokens) {
    return res.redirect('/login');
  }
  
  // Check if token has expired
  if (Date.now() > req.session.tokens.expires_at) {
    try {
      // Refresh the token
      const refreshResponse = await axios.post(config.token_url,
        new URLSearchParams({
          grant_type: 'refresh_token',
          client_id: config.client_id,
          client_secret: config.client_secret,
          refresh_token: req.session.tokens.refresh_token
        }).toString(),
        {
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
          }
        }
      );
      
      // Update tokens in session
      req.session.tokens = refreshResponse.data;
      req.session.tokens.expires_at = Date.now() + refreshResponse.data.expires_in * 1000;
    } catch (error) {
      return res.redirect('/login');
    }
  }
  
  next();
}

// Protected API route
app.get('/api/resources', checkToken, async (req, res) => {
  try {
    const apiResponse = await axios.get(`${config.api_url}/resources`, {
      headers: {
        'Authorization': `Bearer ${req.session.tokens.access_token}`
      }
    });
    
    res.json(apiResponse.data);
  } catch (error) {
    res.status(500).json({ error: 'Failed to fetch resources' });
  }
});

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

Client Credentials Flow

The Client Credentials flow is used for server-to-server authentication when you're accessing your own resources, not user resources. This flow is simpler than the others as it doesn't involve user interaction.

// Node.js example of Client Credentials flow
const axios = require('axios');
const qs = require('querystring');

async function getClientCredentialsToken() {
  try {
    const tokenResponse = await axios.post('https://auth.myappapi.com/oauth/token',
      qs.stringify({
        grant_type: 'client_credentials',
        client_id: 'YOUR_CLIENT_ID',
        client_secret: 'YOUR_CLIENT_SECRET',
        scope: 'read write'
      }),
      {
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded'
        }
      }
    );
    
    return tokenResponse.data.access_token;
  } catch (error) {
    console.error('Error obtaining access token:', error);
    throw error;
  }
}

async function makeApiRequest() {
  try {
    const accessToken = await getClientCredentialsToken();
    
    const apiResponse = await axios.get('https://api.myappapi.com/v1/resources', {
      headers: {
        'Authorization': `Bearer ${accessToken}`
      }
    });
    
    return apiResponse.data;
  } catch (error) {
    console.error('API request failed:', error);
    throw error;
  }
}

JWT Authentication

JSON Web Tokens (JWT) provide a compact and self-contained way for securely transmitting information between parties. MyAppAPI supports JWT as a modern approach to authentication, particularly well-suited for microservices and mobile applications.

JWT Authentication Flow

The JWT authentication flow typically involves:

  1. Client authenticates with username and password (or other credentials)
  2. Server validates credentials and issues a signed JWT
  3. Client includes the JWT in subsequent API requests
  4. Server validates the JWT signature and grants access

JWT Structure

A JWT consists of three parts, separated by dots:

  1. Header: Specifies the token type and signing algorithm
  2. Payload: Contains the claims (user data and metadata)
  3. Signature: Ensures the token hasn't been tampered with

JWT Claims

MyAppAPI JWTs include the following standard claims:

  • iss (Issuer): Identifies the token issuer
  • sub (Subject): Identifies the subject of the token (typically user ID)
  • aud (Audience): Identifies the intended recipients
  • exp (Expiration): Timestamp when the token expires
  • iat (Issued At): Timestamp when the token was issued
  • jti (JWT ID): Unique identifier for the token

Additionally, we include custom claims:

  • scope: Space-separated list of permissions granted to the token
  • client_id: Identifier of the client application

Obtaining JWTs

JWTs are typically obtained through our OAuth 2.0 endpoints. When you request an access token using any of the OAuth flows, you'll receive a JWT as the access token.

Using JWTs with API Requests

To use a JWT with API requests, include it in the Authorization header with the Bearer prefix:

GET /v1/resources HTTP/1.1
Host: api.myappapi.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

JWT Security Considerations

  • Never store sensitive information in JWT claims as they are easily decoded (though not easily modified without the signing key).
  • Always verify the signature on the server before trusting the claims.
  • Set appropriate expiration times to limit the window of opportunity for token misuse.
  • Store tokens securely on the client, preferably in memory rather than persistent storage.
  • Implement token revocation strategies for cases where tokens need to be invalidated before expiration.

JWT Implementation Example

// React example using JWT authentication
import { useState, useEffect, createContext, useContext } from 'react';

// Create an Auth context
const AuthContext = createContext(null);

export function AuthProvider({ children }) {
  const [accessToken, setAccessToken] = useState(null);
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    // Check for existing token on mount
    const token = localStorage.getItem('access_token');
    if (token) {
      // Verify token is not expired
      const payload = parseJwt(token);
      if (payload.exp * 1000 > Date.now()) {
        setAccessToken(token);
        setUser({
          id: payload.sub,
          name: payload.name,
          email: payload.email,
        });
      } else {
        // Token expired, clear it
        localStorage.removeItem('access_token');
      }
    }
    setLoading(false);
  }, []);
  
  // Parse JWT without validation (validation happens on the server)
  const parseJwt = (token) => {
    try {
      return JSON.parse(atob(token.split('.')[1]));
    } catch (e) {
      return null;
    }
  };
  
  // Login function
  const login = async (email, password) => {
    try {
      const response = await fetch('https://auth.myappapi.com/api/token', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ email, password })
      });
      
      if (!response.ok) {
        throw new Error('Login failed');
      }
      
      const data = await response.json();
      const { access_token } = data;
      
      // Store token and extract user info
      localStorage.setItem('access_token', access_token);
      setAccessToken(access_token);
      
      const payload = parseJwt(access_token);
      setUser({
        id: payload.sub,
        name: payload.name,
        email: payload.email,
      });
      
      return true;
    } catch (error) {
      console.error('Login error:', error);
      return false;
    }
  };
  
  // Logout function
  const logout = () => {
    localStorage.removeItem('access_token');
    setAccessToken(null);
    setUser(null);
  };
  
  // API request function with JWT
  const apiRequest = async (endpoint, options = {}) => {
    if (!accessToken) {
      throw new Error('Not authenticated');
    }
    
    // Parse token to check expiration
    const payload = parseJwt(accessToken);
    if (payload.exp * 1000 <= Date.now()) {
      // Token expired, logout and prompt for new login
      logout();
      throw new Error('Session expired. Please login again.');
    }
    
    const config = {
      ...options,
      headers: {
        ...options.headers,
        'Authorization': `Bearer ${accessToken}`,
        'Content-Type': 'application/json'
      }
    };
    
    try {
      const response = await fetch(`https://api.myappapi.com/v1/${endpoint}`, config);
      
      if (!response.ok) {
        const errorData = await response.json();
        throw new Error(errorData.error?.message || 'Request failed');
      }
      
      return response.json();
    } catch (error) {
      console.error('API request error:', error);
      throw error;
    }
  };
  
  return (
    
      {children}
    
  );
}

// Hook to use the auth context
export function useAuth() {
  return useContext(AuthContext);
}

// Example usage in a component
function ResourcesList() {
  const { apiRequest } = useAuth();
  const [resources, setResources] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    async function fetchResources() {
      try {
        const data = await apiRequest('resources');
        setResources(data.data);
        setLoading(false);
      } catch (err) {
        setError(err.message);
        setLoading(false);
      }
    }
    
    fetchResources();
  }, [apiRequest]);
  
  if (loading) return 
Loading...
; if (error) return
Error: {error}
; return (

Resources

    {resources.map(resource => (
  • {resource.name}
  • ))}
); }

Choosing an Authentication Method

Selecting the right authentication method depends on your use case and security requirements:

Use Case Recommended Method Rationale
Backend service or script API Key or Client Credentials Simplicity and direct service-to-service authentication
Mobile application OAuth 2.0 with PKCE Security without exposing client secrets in the app
Single-page application (SPA) OAuth 2.0 with PKCE Security without exposing client secrets in the browser
Traditional web application OAuth 2.0 Authorization Code Flow Full security with client secret stored securely on the server
Microservices architecture JWT Authentication Stateless authentication with standard token format
Third-party application OAuth 2.0 Delegated authorization without sharing user credentials

Security Best Practices

Regardless of which authentication method you choose, follow these security best practices:

Secure Storage of Credentials

  • API Keys and Client Secrets: Store securely as environment variables or in a secure credentials store, never in source code.
  • Access Tokens: In browser environments, prefer in-memory storage over localStorage or sessionStorage when possible.
  • Refresh Tokens: Store with highest security as they have longer lifetimes; use HTTP-only cookies on web servers.

HTTPS Everywhere

Always use HTTPS for API requests and OAuth redirects. Never send credentials or tokens over plain HTTP.

Token Lifetimes

Use appropriate token lifetimes based on security needs:

  • Short lifetimes (minutes to hours) for access tokens
  • Longer lifetimes for refresh tokens with secure storage
  • Regular rotation for API keys

Scope and Permissions

Apply the principle of least privilege by:

  • Requesting only the permissions your application needs
  • Creating different API keys with different permission levels for different functions
  • Regularly auditing and updating permission scopes

Token Validation

When using JWTs, always validate:

  • The token signature to ensure integrity
  • The expiration time to ensure it's not expired
  • The issuer to ensure it's from a trusted source
  • The audience to ensure it's intended for your application

Security Headers

Implement security headers in your application:

  • Content-Security-Policy (CSP)
  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • Strict-Transport-Security (HSTS)

Credential Rotation

Implement regular rotation of:

  • API keys
  • Client secrets
  • Signing keys for JWTs

Incident Response Plan

Prepare for security incidents by:

  • Creating a process for revoking compromised credentials
  • Documenting steps to rotate all affected keys and tokens
  • Setting up monitoring and alerts for suspicious authentication activity

Troubleshooting Authentication Issues

Common authentication problems and how to solve them:

Issue Possible Causes Solutions
401 Unauthorized
  • Missing or invalid credentials
  • Expired token
  • Malformed Authorization header
  • Verify your API key or token is correct
  • Check token expiration and refresh if needed
  • Ensure Authorization header format is correct
403 Forbidden
  • Insufficient permissions
  • Rate limit exceeded
  • Subscription plan restrictions
  • Verify your token has the required scopes
  • Check rate limit headers and adjust request frequency
  • Review your subscription plan limitations
OAuth redirect errors
  • Mismatched redirect URI
  • Incorrect client ID
  • Invalid scope
  • Ensure redirect URI matches exactly what's registered
  • Verify client ID is correct
  • Check that requested scopes are valid
Invalid token errors
  • Token tampering
  • Wrong signing algorithm
  • Clock drift between systems
  • Ensure tokens aren't being modified in transit
  • Verify you're using the correct validation method
  • Synchronize clocks or add time tolerance

Debugging Authentication

When troubleshooting authentication issues:

  1. Examine the complete error response from the API, which often contains specific error details.
  2. Use developer tools to inspect network requests and ensure headers are being sent correctly.
  3. For JWT issues, decode the token at jwt.io to inspect claims (never paste production tokens with sensitive data).
  4. Check API logs in your MyAppAPI Dashboard for additional error context.

If you continue to experience authentication issues, please contact our support team with details about the problem, including request headers, error responses, and steps to reproduce the issue.