Authorization - alxspiker/Pi-Network-Developer-Docs GitHub Wiki

🔐 Authorization & Authentication

Secure user authentication and API authorization patterns for Pi Network applications

Implement robust security with Pi Network's authentication systems

🎯 Overview

🔑 Two main authorization methods for different use cases

🔐 Method 📝 Use Case 🔧 Implementation
Access Tokens User-specific API calls Frontend SDK authentication
Server API Keys Backend server operations Server-to-server requests

🎫 Access Token (Bearer Token)

👤 For user-specific operations requiring Pioneer data

When to Use:

  • Getting user profile information
  • User-specific payment queries
  • Any operation requiring user consent
  • Frontend-to-API communication

📝 Implementation

Header Format:

Authorization: Bearer <Pioneer's access token>

Example Usage:

JavaScript/Node.js Example
// Frontend - obtain token via Pi SDK
const auth = await Pi.authenticate(['payments'], onIncompletePaymentFound);
const accessToken = auth.accessToken;

// Make authenticated API call
const response = await fetch('https://api.minepi.com/v2/me', {
  method: 'GET',
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'Content-Type': 'application/json'
  }
});

const userData = await response.json();
console.log('Authenticated user:', userData);
Python Example
import requests

# Access token obtained from frontend Pi SDK authentication
access_token = "pioneer_access_token_from_frontend"

headers = {
    "Authorization": f"Bearer {access_token}",
    "Content-Type": "application/json"
}

response = requests.get("https://api.minepi.com/v2/me", headers=headers)

if response.status_code == 200:
    user_data = response.json()
    print(f"Authenticated user: {user_data}")
else:
    print(f"Authentication failed: {response.status_code}")

🌐 Hybrid App Authentication

⚠️ Critical: Pi Authentication only works in Pi Browser - proper detection prevents user confusion

Hybrid App Best Practices:

  • Always detect Pi Browser first before calling Pi.authenticate()
  • Show login options only in Pi Browser to avoid scary popups
  • User-initiated authentication - never auto-authenticate on page load
  • Provide clear fallback messaging for non-Pi Browser users

💻 Complete Implementation Example

Modern Hybrid Authentication Pattern
// ✅ Production-ready hybrid authentication
async function initializeAuth() {
  // 1. Initialize Pi SDK
  Pi.init({ version: "2.0", sandbox: false });
  
  // 2. Detect Pi Browser environment
  const isPiBrowser = await detectPiBrowser();
  
  if (!isPiBrowser) {
    // 3a. Show fallback for regular browsers
    showAuthFallback();
    return;
  }
  
  // 3b. Enable Pi authentication for Pi Browser users
  enablePiAuth();
}

function showAuthFallback() {
  document.getElementById('auth-section').innerHTML = `
    <div class="auth-fallback">
      <h3>🔐 Pi Network Login</h3>
      <p>Secure authentication is available in Pi Browser:</p>
      
      <div class="cta-section">
        <a href="https://pinet.com/YOUR_APP" class="pi-button">
          Continue in Pi Browser
        </a>
      </div>
      
      <div class="info-section">
        <h4>Why Pi Browser?</h4>
        <ul>
          <li>✅ Secure Pi Network authentication</li>
          <li>✅ Access to your Pi wallet</li>
          <li>✅ Full app functionality</li>
        </ul>
      </div>
    </div>
  `;
}

function enablePiAuth() {
  // Show login button only in Pi Browser
  document.getElementById('login-button').style.display = 'block';
  document.getElementById('login-button').onclick = handleLogin;
}

async function handleLogin() {
  try {
    // Only authenticate when user clicks login
    const auth = await Pi.authenticate(['payments'], onIncompletePaymentFound);
    
    console.log('✅ Authentication successful:', auth);
    
    // Send access token to your backend for verification
    await verifyUserOnServer(auth.accessToken, auth.user.uid);
    
    // Update UI for authenticated user
    updateUIForAuthenticatedUser(auth);
    
  } catch (error) {
    console.error('❌ Authentication failed:', error);
    handleAuthError(error);
  }
}

async function verifyUserOnServer(accessToken, uid) {
  const response = await fetch('/api/verify-user', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${accessToken}`
    },
    body: JSON.stringify({ uid: uid })
  });
  
  if (!response.ok) {
    throw new Error(`Server verification failed: ${response.status}`);
  }
  
  return await response.json();
}

function updateUIForAuthenticatedUser(auth) {
  document.getElementById('user-info').innerHTML = `
    <h3>Welcome!</h3>
    <p>User ID: ${auth.user.uid}</p>
    <p>Authenticated with scopes: ${auth.user.credentials.scopes.join(', ')}</p>
  `;
  
  // Show authenticated user features
  document.getElementById('authenticated-features').style.display = 'block';
}

function handleAuthError(error) {
  document.getElementById('auth-error').innerHTML = `
    <div class="error-message">
      <h4>Authentication Error</h4>
      <p>${error.message}</p>
      <button onclick="handleLogin()">Try Again</button>
    </div>
  `;
}

function onIncompletePaymentFound(payment) {
  console.log('Found incomplete payment:', payment);
  // Handle incomplete payment restoration
}

// Initialize when DOM is ready
document.addEventListener('DOMContentLoaded', initializeAuth);
});
if (response.ok) {
  console.log('User authenticated successfully');
  updateUIForLoggedInUser(auth);
}

} catch (error) { console.error('Authentication failed:', error); } }

function showPiBrowserRequired() { document.getElementById('auth-section').innerHTML = <div class="pi-browser-required"> <h3>Login Available in Pi Browser</h3> <p>To access your Pi account, please open this app in Pi Browser:</p> <a href="https://pinet.com/YOUR_APP_LINK" class="pi-button"> Open in Pi Browser </a> </div>; }

// ❌ Bad: Don't do this - causes popups in regular browsers // Pi.authenticate(['payments'], callback); // Called on page load


### Backend Token Verification (Unchanged)

The backend verification process remains the same regardless of whether your app is hybrid:

## Obtaining an Access Token

The `Pi.Authenticate` function of the Pi SDK provides an `AuthResults` object:

```js
AuthResults{
    accessToken: string,
    user: {
    uid: string 
    }
}

Security Note: Use the accessToken from the frontend only for verification with the /me API endpoint. Do not store it long-term for identifying the Pioneer, as it is dynamic.

Verifying the Access Token

  1. Pass to Backend: Send the accessToken from your frontend to your server.

  2. Call /me Endpoint: Make a request to the Pi API's /me endpoint using the following header format:

    Authorization: Bearer <Pioneer's access token>
    

    Example (Python with Axios):

    const PioneerAccessToken = accessToken_Obtained_from_App_Frontend;
    const header = { headers: { authorization: "Bearer " + PioneerAccessToken }};
    axios.get("https://api.minepi.com/v2/me", header);
  3. Handle Response:

    • Success (200): The /me endpoint returns a UserDTO object containing the verified uid:

      Object{
        user: {
          uid: string, 
          username: string 
        }
      }
    • Error (401): The Access Token is invalid.

Using the uid within your App

  • Create Unique Records: The verified uid from the /me endpoint can reliably create unique records in your app's database.
  • Personalized Experience: Use the uid to retrieve stored information like purchases or progress, enabling seamless login-free personalization.

Important: Use the verified uid from the /me endpoint, not the initial uid returned by Pi.Authenticate.

Server API Key (Authorization Key)

Certain API calls require authorization from your app's server-side for security reasons. To use a Server API Key:

  1. Obtain from the Developer Portal: Instructions for generating a Server API Key can be found in the appropriate section of the Developer Portal guide.

  2. Include in Authorization Header: Add the key to your API requests in the following format:

    Authorization: Key <Your App's Server API Key>
    

Example Code (Python: Payments Endpoint)

const postingURL = `https://api.minepi.com/v2/payments/${payment_id}`;
const headers = { headers: { authorization: `Key ${APIKEY}` } };
axios.get(postingURL, null, headers); 

Important Notes

  • Secure Storage: Protect your Server API Key. Store it securely on your server and never expose it in client-side code.
  • Refer to Developer Portal: The Developer Portal will provide the most up-to-date instructions for API Key management and usage within specific API endpoints.

Enhancements

  • Purpose: Add a sentence or two clarifying the types of actions that typically require a Server API Key (e.g., processing payments, accessing sensitive Pioneer data).
  • Link to Developer Portal: Provide a direct link to the relevant section of the Developer Portal for easy reference.
⚠️ **GitHub.com Fallback** ⚠️