The firebase connector provides Firebase Authentication support for Aussom Server applications. It wraps the Google Firebase Admin Java SDK and enables server-side token verification, user management, and email action link generation.
Before using this connector you need:
Place the service account JSON file in your app's app_data/ directory.
Include the firebase module and create an instance with your service account credentials:
include firebase;
include file;
class myapp : AppBase {
private fb = null;
private initFb() {
if (this.fb == null) {
this.fb = new firebase(file.read("app_data/service-account.json"));
}
}
}
The firebase constructor takes the service account JSON as a string. Using file.read() to load it from app_data/ is the recommended approach. Use lazy initialization (as shown above) rather than initializing in the class constructor.
This is the primary use case. Your client app (web or mobile) authenticates users via the Firebase Client SDK and sends the resulting ID token to your Aussom Server for verification.
How the flow works:
public protectedEndpoint(req) {
this.initFb();
// Get the token from the Authorization header
authHeader = req.getReq().headers["authorization"];
if (!authHeader) {
req.setStatusCode(401);
req.send({ error: "Missing Authorization header" }.toJson());
return;
}
// Strip "Bearer " prefix
idToken = authHeader.replace("Bearer ", "");
try {
// Verify the token
token = this.fb.verifyIdToken(idToken);
// token is a map with these keys:
// uid - string, the user's unique ID
// email - string or null
// name - string or null (display name)
// emailVerified - bool
// issuer - string
// picture - string or null (photo URL)
// claims - map with all token claims
req.putHeader("content-type", "application/json");
req.send({ message: "Hello " + token.name, uid: token.uid }.toJson());
} catch(e) {
req.setStatusCode(401);
req.send({ error: "Invalid token", details: "" + e }.toJson());
}
}
Pass true as the second argument to also check if the token has been revoked:
token = this.fb.verifyIdToken(idToken, true);
This makes an additional check against Firebase to ensure the user hasn't had their tokens revoked (e.g., after a password change or account disable).
Retrieve user information by UID, email, or phone number:
// By UID
user = this.fb.getUser("abc123");
// By email
user = this.fb.getUserByEmail("user@example.com");
// By phone number (E.164 format)
user = this.fb.getUserByPhone("+15555551234");
All three return a user map with this structure:
{
uid: "abc123",
email: "user@example.com",
displayName: "John Doe",
phoneNumber: "+15555551234",
photoUrl: "https://example.com/photo.jpg",
emailVerified: true,
disabled: false,
providers: [
{ providerId: "google.com", uid: "...", email: "...", displayName: "..." },
{ providerId: "password", uid: "...", email: "...", displayName: null }
],
customClaims: { role: "admin" },
creationTime: 1700000000000,
lastSignInTime: 1700100000000
}
The providers list shows which sign-in methods the user has linked (e.g., google.com, facebook.com, password for email/password).
user = this.fb.createUser({
email: "newuser@example.com",
password: "securePassword123",
displayName: "New User",
emailVerified: false,
disabled: false
});
// user.uid now contains the new user's UID
All fields are optional. Supported keys:
| Key | Type | Description |
|---|---|---|
email |
string | Email address |
password |
string | Password (min 6 characters) |
displayName |
string | Display name |
phoneNumber |
string | Phone in E.164 format |
photoUrl |
string | Photo URL |
emailVerified |
bool | Whether email is verified |
disabled |
bool | Whether account is disabled |
user = this.fb.updateUser("abc123", {
displayName: "Updated Name",
emailVerified: true
});
Takes the user's UID as the first argument and an options map with the fields to update. Same supported keys as createUser.
this.fb.deleteUser("abc123");
Returns null on success. Throws if the user doesn't exist.
// First page (up to 1000 users)
result = this.fb.listUsers();
// Custom page size
result = this.fb.listUsers("", 50);
// Next page
result = this.fb.listUsers(result.nextPageToken, 50);
Returns a map:
{
users: [ ... ], // list of user maps
nextPageToken: "..." // empty string if no more pages
}
Custom claims are key-value pairs attached to a user that are included in their ID tokens. Use them for role-based access control.
this.fb.setCustomClaims("abc123", {
role: "admin",
permissions: ["read", "write", "delete"],
level: 5
});
Claims are included in the user's next ID token. Existing tokens won't reflect the change until they're refreshed.
After setting claims, they appear in the user record and in verified tokens:
// From a user record
user = this.fb.getUser("abc123");
role = user.customClaims.role; // "admin"
// From a verified token
token = this.fb.verifyIdToken(idToken);
role = token.claims.role; // "admin"
Pass an empty map to remove all custom claims:
this.fb.setCustomClaims("abc123", {});
public adminOnly(req) {
this.initFb();
authHeader = req.getReq().headers["authorization"];
if (!authHeader) {
req.setStatusCode(401);
req.send({ error: "Unauthorized" }.toJson());
return;
}
try {
token = this.fb.verifyIdToken(authHeader.replace("Bearer ", ""));
// Check for admin role in claims
if (token.claims.role != "admin") {
req.setStatusCode(403);
req.send({ error: "Forbidden: admin role required" }.toJson());
return;
}
// User is admin, proceed
req.putHeader("content-type", "application/json");
req.send({ message: "Admin access granted", uid: token.uid }.toJson());
} catch(e) {
req.setStatusCode(401);
req.send({ error: "" + e }.toJson());
}
}
Session cookies are an alternative to ID tokens for web applications. They're longer-lived and can be verified server-side without hitting Firebase on every request.
// Create a cookie that expires in 5 days (in milliseconds)
fiveDaysMs = 5 * 24 * 60 * 60 * 1000;
cookie = this.fb.createSessionCookie(idToken, fiveDaysMs);
// Set it on the response
req.setCookie("session", cookie, fiveDaysMs / 1000, true, "/");
The expiration must be between 5 minutes and 14 days (in milliseconds).
public sessionProtected(req) {
this.initFb();
cookies = req.getReqCookies();
sessionCookie = "";
for (cookie : cookies) {
if (cookie.name == "session") {
sessionCookie = cookie.value;
}
}
if (sessionCookie == "") {
req.setStatusCode(401);
req.send({ error: "No session" }.toJson());
return;
}
try {
token = this.fb.verifySessionCookie(sessionCookie, true);
req.putHeader("content-type", "application/json");
req.send({ uid: token.uid, email: token.email }.toJson());
} catch(e) {
req.setStatusCode(401);
req.send({ error: "Invalid session" }.toJson());
}
}
Custom tokens allow your server to create Firebase tokens for users who authenticate through an external system. The client SDK can then use signInWithCustomToken() to sign in.
// Simple custom token
token = this.fb.createCustomToken("user-uid-123");
// With custom claims embedded in the token
token = this.fb.createCustomToken("user-uid-123", {
premiumAccount: true,
tier: "gold"
});
The client uses this token to sign in:
// Client-side JavaScript
firebase.auth().signInWithCustomToken(customToken);
Generate links for password reset, email verification, and email sign-in. These links can be sent via your own email system (e.g., SendGrid) instead of using Firebase's built-in email templates.
link = this.fb.generatePasswordResetLink("user@example.com");
// Send this link to the user via email
link = this.fb.generateEmailVerificationLink("user@example.com");
// Send this link to the user via email
link = this.fb.generateSignInWithEmailLink("user@example.com", {
url: "https://myapp.com/complete-signin",
handleCodeInApp: true
});
include firebase;
include sendgrid;
include file;
class myapp : AppBase {
private fb = null;
private sgKey = "encrypted-sendgrid-key-here";
private initFb() {
if (this.fb == null) {
this.fb = new firebase(file.read("app_data/service-account.json"));
}
}
public resetPassword(req) {
this.initFb();
body = json.parse(req.getBody());
email = body.email;
try {
link = this.fb.generatePasswordResetLink(email);
// Send via SendGrid
sg = new sendgrid(props.decrypt(this.sgKey));
sg
.from("noreply@myapp.com")
.to(email)
.subject("Reset Your Password")
.content("Click here to reset your password: " + link)
.send()
;
req.putHeader("content-type", "application/json");
req.send({ status: "Reset email sent" }.toJson());
} catch(e) {
req.setStatusCode(500);
req.send({ error: "" + e }.toJson());
}
}
}
Force a user to re-authenticate on all devices by revoking their refresh tokens:
this.fb.revokeRefreshTokens("abc123");
After revoking, use verifyIdToken(token, true) with the revocation check enabled to reject tokens issued before the revocation.
All firebase methods throw exceptions on failure. Catch them with try/catch and convert to string with "" + e to get the error message:
try {
user = this.fb.getUser("nonexistent-uid");
} catch(e) {
estr = "" + e;
// estr contains: "firebase.getUser(): USER_NOT_FOUND: ..."
}
Error messages follow the format: firebase.<method>(): <ERROR_CODE>: <message>
| Error Code | Meaning |
|---|---|
USER_NOT_FOUND |
No user with the given identifier |
EMAIL_ALREADY_EXISTS |
Email is already in use |
INVALID_ID_TOKEN |
Token is malformed |
EXPIRED_ID_TOKEN |
Token has expired |
REVOKED_ID_TOKEN |
Token was revoked |
INVALID_SESSION_COOKIE |
Session cookie is invalid |
EXPIRED_SESSION_COOKIE |
Session cookie has expired |
REVOKED_SESSION_COOKIE |
Session cookie was revoked |
try {
user = this.fb.createUser({ email: "taken@example.com", password: "pass123" });
} catch(e) {
estr = "" + e;
if (estr.contains("EMAIL_ALREADY_EXISTS")) {
// Handle duplicate email
req.setStatusCode(409);
req.send({ error: "Email already in use" }.toJson());
} else {
req.setStatusCode(500);
req.send({ error: estr }.toJson());
}
}
A small but complete app showing the common authentication patterns:
include firebase;
include file;
@API(
version = "1.0.0",
contactName = "My App",
contactUrl = "https://myapp.com"
)
class myapp : AppBase {
private fb = null;
private initFb() {
if (this.fb == null) {
this.fb = new firebase(file.read("app_data/service-account.json"));
}
}
/**
* GET / - Public endpoint, no auth required.
*/
public index(req) {
req.putHeader("content-type", "application/json");
req.send({ status: "ok", message: "Welcome" }.toJson());
}
/**
* POST /signup - Create a new user account.
* Body: { "email": "...", "password": "...", "name": "..." }
*/
public signup(req) {
this.initFb();
try {
body = json.parse(req.getBody());
user = this.fb.createUser({
email: body.email,
password: body.password,
displayName: body.name
});
// Set default role
this.fb.setCustomClaims(user.uid, { role: "user" });
req.putHeader("content-type", "application/json");
req.send({
uid: user.uid,
email: user.email,
displayName: user.displayName
}.toJson());
} catch(e) {
estr = "" + e;
if (estr.contains("EMAIL_ALREADY_EXISTS")) {
req.setStatusCode(409);
req.send({ error: "Email already registered" }.toJson());
} else {
req.setStatusCode(400);
req.send({ error: estr }.toJson());
}
}
}
/**
* GET /profile - Get the authenticated user's profile.
* Requires: Authorization: Bearer <idToken>
*/
public profile(req) {
this.initFb();
authHeader = req.getReq().headers["authorization"];
if (!authHeader) {
req.setStatusCode(401);
req.send({ error: "Missing Authorization header" }.toJson());
return;
}
try {
token = this.fb.verifyIdToken(authHeader.replace("Bearer ", ""));
user = this.fb.getUser(token.uid);
req.putHeader("content-type", "application/json");
req.send({
uid: user.uid,
email: user.email,
displayName: user.displayName,
emailVerified: user.emailVerified,
role: user.customClaims.role,
providers: user.providers
}.toJson());
} catch(e) {
req.setStatusCode(401);
req.send({ error: "Authentication failed" }.toJson());
}
}
/**
* POST /promote - Promote a user to admin (admin only).
* Requires: Authorization: Bearer <idToken> (must be admin)
* Body: { "uid": "..." }
*/
public promote(req) {
this.initFb();
authHeader = req.getReq().headers["authorization"];
if (!authHeader) {
req.setStatusCode(401);
req.send({ error: "Unauthorized" }.toJson());
return;
}
try {
// Verify the caller is an admin
callerToken = this.fb.verifyIdToken(authHeader.replace("Bearer ", ""));
if (callerToken.claims.role != "admin") {
req.setStatusCode(403);
req.send({ error: "Admin access required" }.toJson());
return;
}
// Promote the target user
body = json.parse(req.getBody());
this.fb.setCustomClaims(body.uid, { role: "admin" });
req.putHeader("content-type", "application/json");
req.send({ status: "User promoted to admin" }.toJson());
} catch(e) {
req.setStatusCode(500);
req.send({ error: "" + e }.toJson());
}
}
/**
* GET /users - List all users (admin only).
* Requires: Authorization: Bearer <idToken> (must be admin)
*/
public users(req) {
this.initFb();
authHeader = req.getReq().headers["authorization"];
if (!authHeader) {
req.setStatusCode(401);
req.send({ error: "Unauthorized" }.toJson());
return;
}
try {
callerToken = this.fb.verifyIdToken(authHeader.replace("Bearer ", ""));
if (callerToken.claims.role != "admin") {
req.setStatusCode(403);
req.send({ error: "Admin access required" }.toJson());
return;
}
result = this.fb.listUsers("", 100);
req.putHeader("content-type", "application/json");
req.send(result.toJson());
} catch(e) {
req.setStatusCode(500);
req.send({ error: "" + e }.toJson());
}
}
}
| Method | Parameters | Returns |
|---|---|---|
firebase(ServiceAccountJson) |
string - service account JSON |
this object |
| Method | Parameters | Returns |
|---|---|---|
verifyIdToken(IdToken, CheckRevoked) |
string, bool (default: false) |
map - decoded token |
createSessionCookie(IdToken, ExpiresInMs) |
string, int |
string - session cookie |
verifySessionCookie(Cookie, CheckRevoked) |
string, bool (default: false) |
map - decoded token |
createCustomToken(Uid, Claims) |
string, map (default: {}) |
string - custom token |
| Method | Parameters | Returns |
|---|---|---|
getUser(Uid) |
string |
map - user record |
getUserByEmail(Email) |
string |
map - user record |
getUserByPhone(PhoneNumber) |
string (E.164 format) |
map - user record |
createUser(Options) |
map |
map - user record |
updateUser(Uid, Options) |
string, map |
map - user record |
deleteUser(Uid) |
string |
null |
setCustomClaims(Uid, Claims) |
string, map |
null |
revokeRefreshTokens(Uid) |
string |
null |
listUsers(PageToken, MaxResults) |
string (default: ""), int (default: 1000) |
map - {users, nextPageToken} |
| Method | Parameters | Returns |
|---|---|---|
generatePasswordResetLink(Email) |
string |
string - link |
generateEmailVerificationLink(Email) |
string |
string - link |
generateSignInWithEmailLink(Email, Settings) |
string, map |
string - link |