Authorization - alxspiker/Pi-Network-Developer-Docs GitHub Wiki
Implement robust security with Pi Network's authentication systems
🔑 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 |
👤 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
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}")
⚠️ 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
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.
-
Pass to Backend: Send the
accessToken
from your frontend to your server. -
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);
-
Handle Response:
-
Success (200): The
/me
endpoint returns aUserDTO
object containing the verifieduid
:Object{ user: { uid: string, username: string } }
-
Error (401): The Access Token is invalid.
-
-
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
.
Certain API calls require authorization from your app's server-side for security reasons. To use a Server API Key:
-
Obtain from the Developer Portal: Instructions for generating a Server API Key can be found in the appropriate section of the Developer Portal guide.
-
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.