Integrating Passkey Authentication with Contribyard in Next.js (Example)
Prerequisites
Before starting, ensure you have:
- A Next.js application.
- Installed the
@github/webauthn-json
package.
- Access to the Contribyard API.
Step 1: Install Required Dependencies
In your Next.js project, install the necessary npm package:
npm install @github/webauthn-json
Set up your environment variables for Contribyard API access by adding the following to your .env.local
file:
NEXT_PUBLIC_CONTRIBYARD_API_KEY=your_contribyard_api_key
Replace your_contribyard_api_key
with your actual API key from Contribyard.
Step 3: Implement Registration Flow
Create a register.js
page in your Next.js pages
directory for the user registration flow.
import { startRegistration } from "@github/webauthn-json";
export default function Register() {
const handleRegister = async () => {
try {
const response = await fetch("/api/register-challenge", {
method: "POST",
});
const challengeData = await response.json();
const credentials = await startRegistration(challengeData);
const result = await fetch("/api/register", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(credentials),
});
if (result.ok) {
alert("Registration successful!");
} else {
alert("Registration failed!");
}
} catch (error) {
console.error("Error during registration:", error);
}
};
return (
<div>
<h1>Register with Passkey</h1>
<button onClick={handleRegister}>Register</button>
</div>
);
}
Step 4: Create API Endpoints for the Registration Flow
Create two API routes in the pages/api/
directory to interact with Contribyard’s /v1/passkey_attestations
and /v1/passkey_attestations/{id}/verify
endpoints.
register-challenge.js
This endpoint retrieves the attestation options from Contribyard:
export default async function handler(req, res) {
const response = await fetch("https://api.contribyard.com/v1/passkey_attestations", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.NEXT_PUBLIC_CONTRIBYARD_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
attestation: {
user_id: "new_user", // Customize this value as needed
},
}),
});
const attestationOptions = await response.json();
res.status(201).json(attestationOptions.response.credential_creation_options);
}
register.js
This endpoint verifies the attestation:
export default async function handler(req, res) {
const { id, rawId, response, type } = req.body;
const responseBody = {
attestation: {
id,
rawId,
response,
type,
},
};
const verifyResponse = await fetch(`https://api.contribyard.com/v1/passkey_attestations/${id}/verify`, {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.NEXT_PUBLIC_CONTRIBYARD_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(responseBody),
});
if (verifyResponse.ok) {
res.status(201).json({ success: true });
} else {
res.status(500).json({ success: false });
}
}
Step 5: Implement Authentication Flow
Now, let’s implement the authentication flow. Create a login.js
page for user authentication.
import { startAuthentication } from "@github/webauthn-json";
export default function Login() {
const handleLogin = async () => {
try {
const response = await fetch("/api/login-challenge", {
method: "POST",
});
const challengeData = await response.json();
const credentials = await startAuthentication(challengeData);
const result = await fetch("/api/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(credentials),
});
if (result.ok) {
alert("Login successful!");
} else {
alert("Login failed!");
}
} catch (error) {
console.error("Error during login:", error);
}
};
return (
<div>
<h1>Login with Passkey</h1>
<button onClick={handleLogin}>Login</button>
</div>
);
}
Step 6: Create API Endpoints for the Authentication Flow
Create two API routes to handle authentication using the /v1/passkey_assertions
and /v1/passkey_assertions/{id}/verify
endpoints.
login-challenge.js
This endpoint retrieves the assertion challenge from Contribyard:
export default async function handler(req, res) {
const response = await fetch("https://api.contribyard.com/v1/passkey_assertions", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.NEXT_PUBLIC_CONTRIBYARD_API_KEY}`,
"Content-Type": "application/json",
},
});
const assertionOptions = await response.json();
res.status(201).json(assertionOptions.response.publicKey);
}
login.js
This endpoint verifies the assertion:
export default async function handler(req, res) {
const { id, rawId, response, type } = req.body;
const responseBody = {
assertion: {
id,
rawId,
response,
type,
},
};
const verifyResponse = await fetch(`https://api.contribyard.com/v1/passkey_assertions/${id}/verify`, {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.NEXT_PUBLIC_CONTRIBYARD_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(responseBody),
});
if (verifyResponse.ok) {
res.status(200).json({ success: true });
} else {
res.status(500).json({ success: false });
}
}
Conclusion
By following this guide, you have successfully integrated Passkey authentication using Contribyard’s API with Next.js. You can now extend this with better error handling, user feedback, and production-level security features. For further customization, refer to the Contribyard API documentation.