DoneIsBetter SSO Integration Guide
API Version: 5.24.0
OAuth 2.0 Authorization Server
⚠️ Important: OAuth 2.0 Flow
This SSO service uses OAuth 2.0 Authorization Code Flow. The old client-side approach has been deprecated. You must implement server-side token exchange and handle app-level permissions.
Overview
DoneIsBetter SSO is an OAuth 2.0 authorization server that provides:
- Centralized Authentication - Users log in once, access multiple apps
- App-Level Permissions - Control which users can access which applications
- Role-Based Access - Assign users as
useroradminper app - Admin Approval Workflow - New users require SSO admin approval before accessing apps
- Secure Token Management - JWT-based access and refresh tokens
Authentication Flow
User Clicks "Login"
Your app redirects to SSO authorization endpoint:
https://sso.doneisbetter.com/api/oauth/authorize? client_id=YOUR_CLIENT_ID& redirect_uri=https://yourapp.com/auth/callback& response_type=code& scope=openid profile emailUser Authenticates
SSO presents login page. User enters credentials or uses magic link/PIN.
Permission Check
SSO verifies user has approved access to your app:
status: 'approved'→ Continue to step 4status: 'pending'→ Show "Access Pending Approval" messagestatus: 'revoked'or no permission → Show "Access Denied"
Authorization Code
SSO redirects back to your app with authorization code:
https://yourapp.com/auth/callback?code=AUTHORIZATION_CODEToken Exchange (Server-Side)
Your backend exchanges code for tokens:
POST https://sso.doneisbetter.com/api/oauth/token Content-Type: application/json { "grant_type": "authorization_code", "code": "AUTHORIZATION_CODE", "client_id": "YOUR_CLIENT_ID", "client_secret": "YOUR_CLIENT_SECRET", "redirect_uri": "https://yourapp.com/auth/callback" }Receive Tokens
SSO returns access token, ID token, and refresh token:
{ "access_token": "eyJhbGci...", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "eyJhbGci...", "id_token": "eyJhbGci..." }Decode ID token to get user info and app-level role:
{ "sub": "user-uuid", "email": "user@example.com", "name": "User Name", "role": "admin", // App-level role: 'user' or 'admin' "iat": 1234567890, "exp": 1234571490 }
Integration Steps
Register Your Domain
Contact us at support@doneisbetter.com to register your domain.
Required information:
- Your domain name
- Organization name
- Technical contact email
- Development and production URLs
Add SSO Client
Include our client script in your HTML:
<script src="https://sso.doneisbetter.com/sso-client.js"></script>Initialize SSO
Create an SSO instance and check authentication:
const sso = new SSOClient('https://sso.doneisbetter.com'); // Check on page load document.addEventListener('DOMContentLoaded', async () => { const session = await sso.validateSession(); if (session.isValid) { // Handle authenticated user console.log('User:', session.user); } else { // Handle unauthenticated state sso.redirectToLogin(); } });
API Reference
Session Validation
Endpoint: GET https://sso.doneisbetter.com/api/sso/validate
Headers:
Content-Type: application/jsonOrigin: your-domain.com
Success Response (200 OK)
{
"isValid": true,
"user": {
"id": "user_id",
"username": "username",
"permissions": {
"isAdmin": false,
"canViewUsers": false,
"canManageUsers": false
}
},
"session": {
"expiresAt": "2025-07-21T14:43:47Z"
}
}Error Responses
401- Invalid or expired session403- Unauthorized domain500- Server error
Security Best Practices
- Always use HTTPS in production
- Validate session status before accessing protected resources
- Implement proper error handling
- Don't store sensitive user data in localStorage
- Keep the SSO client script updated to the latest version
Example Implementations
React
import { useEffect, useState } from 'react';
function App() {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function checkAuth() {
const sso = new SSOClient('https://sso.doneisbetter.com');
try {
const result = await sso.validateSession();
if (result.isValid) {
setUser(result.user);
} else {
sso.redirectToLogin();
}
} catch (error) {
console.error('Auth error:', error);
} finally {
setLoading(false);
}
}
checkAuth();
}, []);
if (loading) return <div>Loading...</div>;
if (!user) return null;
return <div>Welcome, {user.username}!</div>;
}Vue.js
// auth.js
export default {
data() {
return {
user: null,
loading: true
}
},
async created() {
const sso = new SSOClient('https://sso.doneisbetter.com');
try {
const result = await sso.validateSession();
if (result.isValid) {
this.user = result.user;
} else {
sso.redirectToLogin();
}
} catch (error) {
console.error('Auth error:', error);
} finally {
this.loading = false;
}
}
}Support
For technical support or to report issues:
- Email: support@doneisbetter.com
- Documentation: https://sso.doneisbetter.com/docs
- GitHub: https://github.com/doneisbetter/sso