Pi Network Authentication Guide
Welcome to the implementation guide for Pi Network Authentication. This page covers the Pi SDK (window.Pi) delivered from https://sdk.minepi.com/pi-sdk.js.
Overview
Authentication is the entry point for any Pi App. It allows you to:
- Verify the user’s identity via the Pi Browser.
- Obtain a unique UID and Username.
- Secure an AccessToken for server-side validation.
Setup & Installation
Include the Pi SDK
Ensure the Pi SDK is available in your HTML <head>. This provides the bridge to the Pi Browser.
<head>
<!-- ... other <head> content (meta, title, styles, etc.) ... -->
<script src="https://sdk.minepi.com/pi-sdk.js"></script>
</head>
Initialize it once on page load, before any other Pi SDK calls:
Pi.init({ version: "2.0", sandbox: true }); // use sandbox: false in production
See the Client SDK reference for Pi.init options.
Backend verification
Authentication is only complete once verified on the server. Send the accessToken returned by Pi.authenticate() to your backend (Node.js, Python, Go, etc.).
- Token Validation: Your backend should call the Pi Platform API
/meendpoint withAuthorization: Bearer <accessToken>. - Session Management: Once the Pi API confirms the UID, your server can issue a JWT or session cookie for your specific app.
You can wrap window.Pi in your frontend framework’s state layer (React context/hooks, Vue/Svelte stores, Angular services). Always call Pi.authenticate() — do not invent an alternate auth path.
Implementation: The PiService Pattern
Wrapping your authentication logic in a service class keeps it in one place, makes it easier to test, and stops auth calls from being scattered across components.
The Service Wrapper
/**
* PiService handles all communication with the Pi Network.
* This abstraction allows for easier testing and future feature expansion.
*/
class PiService {
constructor() {
this.user = null;
}
/**
* Authenticates the user and requests specific data scopes.
* @param {string[]} scopes - Defaults to ['username']
*/
async login(scopes = ['username']) {
try {
const auth = await window.Pi.authenticate(scopes, this.onIncompletePaymentFound);
this.user = auth.user;
console.log(`Authenticated as ${this.user.username}`);
return {
success: true,
accessToken: auth.accessToken,
user: auth.user
};
} catch (error) {
console.error("Authentication Error:", error);
return { success: false, error: error.message };
}
}
/**
* Required callback for Pi SDK.
* Handles payments that were interrupted before completion.
*/
onIncompletePaymentFound(payment) {
console.warn("Incomplete payment found:", payment.identifier);
// Logic to resolve this on the backend should be added here.
}
}
export const piService = new PiService();
Integration Logic
To add login to a page:
- Event Binding: Attach a listener to a “Login” button.
- Call Service: Execute
piService.login(). - UI Update: Transition the interface from “Guest” to “User” state using the returned
user.username. - Token Exchange: Send the
accessTokento your application backend for verification against the Pi API.
Example UI Trigger
async function handleLogin() {
const result = await piService.login(['username', 'payments']);
if (result.success) {
document.getElementById('user-profile').innerText = `Hello, ${result.user.username}`;
} else {
alert("Could not sign in: " + result.error);
}
}
Best practices
- Use
async/awaitwithPi.authenticate()rather than nesting callbacks. - Wrap authentication in
try/catchso a user cancelling the flow doesn't surface as an unhandled rejection. - Request the narrowest scopes you need. Only ask for
paymentswhen your app actually charges the user — over-requesting costs you trust. - Enable sandbox mode in development if you are testing outside the Pi Browser.
- Treat your server as the source of truth. Never authorize off client-side
userdata; verify theaccessTokenagainst/meon your backend.