@binary1702/auth 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,73 @@
1
+ // ─────────────────────────────────────────────────────────────
2
+ // Where the sign-in and sign-up pages live. Pure.
3
+ //
4
+ // The package serves plain HTML pages under /auth/. An app that wants
5
+ // its own design names its pages in b17_auth.yaml:
6
+ //
7
+ // pages:
8
+ // signIn: /sign-in
9
+ // signUp: /sign-up
10
+ //
11
+ // Then every place the package sends a browser (the proxy, sign-out,
12
+ // a verified email, "create the first account") goes to the app's page,
13
+ // and GET /auth/sign-in redirects there too, so old links still land.
14
+ // The protocol does not move: the app's page still POSTs to
15
+ // /auth/sign-in and /auth/sign-up (JSON or a form). The package owns
16
+ // the protocol; the app owns the pixels.
17
+ // ─────────────────────────────────────────────────────────────
18
+ // The built-in paths live here (not in config.ts, which imports options.ts,
19
+ // which imports this file): no import cycle at module load.
20
+ export const SIGN_IN_PATH = "/auth/sign-in";
21
+ export const SIGN_UP_PATH = "/auth/sign-up";
22
+ export const PAGE_KEYS = ["signIn", "signUp"];
23
+ const BUILT_IN = { signIn: SIGN_IN_PATH, signUp: SIGN_UP_PATH };
24
+ /** The app's path for a page, or undefined when the package serves it */
25
+ export function pageOverride(pages, key) {
26
+ return pages?.[key];
27
+ }
28
+ /** Where a browser is sent for a page: the app's path if named, else the package's */
29
+ export function pagePath(pages, key) {
30
+ return pageOverride(pages, key) ?? BUILT_IN[key];
31
+ }
32
+ /** The app's pages, for the proxy: they must open without a session */
33
+ export function appPages(pages) {
34
+ return PAGE_KEYS.map((k) => pages?.[k]).filter((p) => typeof p === "string");
35
+ }
36
+ /**
37
+ * An app path is an absolute path on this site, outside /auth/ (which the
38
+ * package serves), with no pattern, query or fragment. Error text or null.
39
+ */
40
+ export function validateAppPath(key, path) {
41
+ if (!path.startsWith("/"))
42
+ return `${key} must be a path starting with /, not "${path}"`;
43
+ if (/^\/auth(\/|$)/.test(path))
44
+ return `${key} cannot be under /auth/, which the package serves`;
45
+ if (/[*?#\s]/.test(path))
46
+ return `${key} is one plain path, no * ? # or spaces: "${path}"`;
47
+ return null;
48
+ }
49
+ export function validatePagePath(key, path) {
50
+ return validateAppPath(`pages.${key}`, path);
51
+ }
52
+ /**
53
+ * Where a signed-in visitor lands when nothing says otherwise: `home:` from
54
+ * b17_auth.yaml (the app's signed-in home, say /app), else the site root.
55
+ * after sign-in with no `next`, or `next` reduced to "/" by safeNext
56
+ * the admin page's "app" link
57
+ * a signed-in visitor opening "/" (the proxy sends them home: the root is
58
+ * the public site, the app is where they were going)
59
+ */
60
+ export function landing(next, home) {
61
+ return next && next !== "/" ? next : (home ?? "/");
62
+ }
63
+ /** Should the proxy send this signed-in request home? Only the root, only when home is elsewhere. */
64
+ export function sendsHome(pathname, home) {
65
+ return pathname === "/" && !!home && home !== "/";
66
+ }
67
+ /**
68
+ * The same URL on the app's page: GET /auth/sign-in?next=/x&error=1 becomes
69
+ * /sign-in?next=/x&error=1, so a bookmark or an old link keeps working.
70
+ */
71
+ export function movedTo(path, search) {
72
+ return path + search;
73
+ }
@@ -14,6 +14,10 @@ export declare const PASSWORD_MIN = 8;
14
14
  export declare const PASSWORD_MAX = 256;
15
15
  /** Returns an error message, or null when the password is acceptable. */
16
16
  export declare function validatePassword(password: string): string | null;
17
+ /** Returns an error message, or null when the address is acceptable (shape, and the domain rule if any). */
18
+ export declare function validateEmail(email: string, opts?: {
19
+ emailDomain?: string;
20
+ }): string | null;
17
21
  /** Returns an error message, or null when the pair is acceptable. */
18
22
  export declare function validateCredentials(email: string, password: string, opts?: {
19
23
  emailDomain?: string;
@@ -29,13 +29,17 @@ export function validatePassword(password) {
29
29
  return `Password must be at most ${PASSWORD_MAX} characters.`;
30
30
  return null;
31
31
  }
32
- /** Returns an error message, or null when the pair is acceptable. */
33
- export function validateCredentials(email, password, opts) {
32
+ /** Returns an error message, or null when the address is acceptable (shape, and the domain rule if any). */
33
+ export function validateEmail(email, opts) {
34
34
  if (!EMAIL_RE.test(email))
35
35
  return "Please enter a valid email address.";
36
36
  const domain = opts?.emailDomain;
37
37
  if (domain && !email.toLowerCase().endsWith(`@${domain.toLowerCase()}`)) {
38
38
  return `Only @${domain} emails are allowed.`;
39
39
  }
40
- return validatePassword(password);
40
+ return null;
41
+ }
42
+ /** Returns an error message, or null when the pair is acceptable. */
43
+ export function validateCredentials(email, password, opts) {
44
+ return validateEmail(email, opts) ?? validatePassword(password);
41
45
  }
package/dist/index.d.ts CHANGED
@@ -1,17 +1,18 @@
1
1
  export { hashPassword, verifyPassword, needsRehash, parseHash, SCRYPT_N } from "./core/password.ts";
2
2
  export { isCurrent, signSession, verifySession, claimsAcceptable, ISSUER, AUDIENCE, CLAIMS_REQUIRED_FROM, type Claim, type User } from "./core/session.ts";
3
- export { db, ensureSchema, pendingSteps, MIGRATIONS, findUser, createUser, countUsers, listUsers, setPassword, setPasswordById, sessionEpoch, userState, sessionRow, markVerified, revokeSessions, removeUser, createOrg, findOrg, listOrgs, addMember, removeMember, removeOrg, adminUsers, adminOrgs, setActiveOrg, type Db, type UserState, type SessionRow, type OrgRow, type Migration, type Statement, type UserRow, } from "./core/db.ts";
3
+ export { db, ensureSchema, pendingSteps, MIGRATIONS, findUser, createUser, countUsers, listUsers, setPassword, setPasswordById, sessionEpoch, userState, sessionRow, markVerified, revokeSessions, removeUser, createOrg, findOrg, listOrgs, addMember, removeMember, removeOrg, adminUsers, adminOrgs, setActiveOrg, storedRoles, setRolePermissions, resetRole, seedRoles, memberRole, type StoredRole, type Db, type UserState, type SessionRow, type OrgRow, type Migration, type Statement, type UserRow, } from "./core/db.ts";
4
4
  export { signInPage, signUpPage, forgotPage, resetPage, messagePage, securityHeaders } from "./core/html.ts";
5
5
  export { resetLink, resetEmail, FORGOT_PATH, RESET_PATH, RESET_TTL_SECONDS } from "./core/reset.ts";
6
6
  export { mayAccess, verifyLink, verifyEmail, VERIFY_PATH, VERIFY_TTL_SECONDS } from "./core/verify.ts";
7
- export { options, loadOptions, resolveOptions, resetOptions, literalSecrets, CONFIG_FILE, DEFAULTS, type Options, type EmailOptions, type SignupMode, } from "./core/options.ts";
7
+ export { options, loadOptions, resolveOptions, resetOptions, literalSecrets, looksLikeRoleMap, PERMISSION_ID, CONFIG_FILE, DEFAULTS, type Options, type EmailOptions, type SignupMode, } from "./core/options.ts";
8
8
  export { parseYaml, YamlError, type YamlTree } from "./core/yaml.ts";
9
- export { isAdmin, refusedOnSelf, roleChoices, ADMIN_PATH } from "./core/admin.ts";
9
+ export { isAdmin, mayEditRoles, refusedOnSelf, roleChoices, checkedFor, grantedFromFields, ADMIN_PATH, ROLE_NAME, ROLE_ACTIONS } from "./core/admin.ts";
10
10
  export { rateLimit, decide, clientIp, bucketKey, retryMessage, LIMITS, type Bucket, type Decision } from "./core/rate.ts";
11
11
  export { adminPage } from "./core/admin-html.ts";
12
- export { buildSession, hasPermission, permissionsFor, NO_SESSION, type Session, type Organization, type Authorization, type PermissionSource, } from "./core/org.ts";
12
+ export { buildSession, hasPermission, permissionsFor, NO_SESSION, OWNER, type Session, type Organization, type Authorization, type PermissionSource, type StoredGrants, } from "./core/org.ts";
13
13
  export { sendEmail, emailTransport, type EmailMessage, type Transport } from "./core/email.ts";
14
14
  export { issueToken, consumeToken, hashToken, tokenState, type Token, type TokenKind } from "./core/tokens.ts";
15
15
  export { signupPolicy, validateCredentials, validatePassword, PASSWORD_MIN, PASSWORD_MAX, type SignupPolicy } from "./core/signup.ts";
16
16
  export { safeNext, safeEqual, isPublic, matchesPattern, isSameOrigin, MATCHER } from "./core/guard.ts";
17
+ export { pagePath, pageOverride, appPages, validatePagePath, validateAppPath, landing, sendsHome, PAGE_KEYS, type Pages, type PageKey } from "./core/pages.ts";
17
18
  export { COOKIE_NAME, cookieName, SESSION_DAYS, SIGN_IN_PATH, SIGN_OUT_PATH, SIGN_UP_PATH, getSecret, getDatabaseUrl } from "./core/config.ts";
package/dist/index.js CHANGED
@@ -2,18 +2,19 @@
2
2
  // or want to build your own handlers.
3
3
  export { hashPassword, verifyPassword, needsRehash, parseHash, SCRYPT_N } from "./core/password.js";
4
4
  export { isCurrent, signSession, verifySession, claimsAcceptable, ISSUER, AUDIENCE, CLAIMS_REQUIRED_FROM } from "./core/session.js";
5
- export { db, ensureSchema, pendingSteps, MIGRATIONS, findUser, createUser, countUsers, listUsers, setPassword, setPasswordById, sessionEpoch, userState, sessionRow, markVerified, revokeSessions, removeUser, createOrg, findOrg, listOrgs, addMember, removeMember, removeOrg, adminUsers, adminOrgs, setActiveOrg, } from "./core/db.js";
5
+ export { db, ensureSchema, pendingSteps, MIGRATIONS, findUser, createUser, countUsers, listUsers, setPassword, setPasswordById, sessionEpoch, userState, sessionRow, markVerified, revokeSessions, removeUser, createOrg, findOrg, listOrgs, addMember, removeMember, removeOrg, adminUsers, adminOrgs, setActiveOrg, storedRoles, setRolePermissions, resetRole, seedRoles, memberRole, } from "./core/db.js";
6
6
  export { signInPage, signUpPage, forgotPage, resetPage, messagePage, securityHeaders } from "./core/html.js";
7
7
  export { resetLink, resetEmail, FORGOT_PATH, RESET_PATH, RESET_TTL_SECONDS } from "./core/reset.js";
8
8
  export { mayAccess, verifyLink, verifyEmail, VERIFY_PATH, VERIFY_TTL_SECONDS } from "./core/verify.js";
9
- export { options, loadOptions, resolveOptions, resetOptions, literalSecrets, CONFIG_FILE, DEFAULTS, } from "./core/options.js";
9
+ export { options, loadOptions, resolveOptions, resetOptions, literalSecrets, looksLikeRoleMap, PERMISSION_ID, CONFIG_FILE, DEFAULTS, } from "./core/options.js";
10
10
  export { parseYaml, YamlError } from "./core/yaml.js";
11
- export { isAdmin, refusedOnSelf, roleChoices, ADMIN_PATH } from "./core/admin.js";
11
+ export { isAdmin, mayEditRoles, refusedOnSelf, roleChoices, checkedFor, grantedFromFields, ADMIN_PATH, ROLE_NAME, ROLE_ACTIONS } from "./core/admin.js";
12
12
  export { rateLimit, decide, clientIp, bucketKey, retryMessage, LIMITS } from "./core/rate.js";
13
13
  export { adminPage } from "./core/admin-html.js";
14
- export { buildSession, hasPermission, permissionsFor, NO_SESSION, } from "./core/org.js";
14
+ export { buildSession, hasPermission, permissionsFor, NO_SESSION, OWNER, } from "./core/org.js";
15
15
  export { sendEmail, emailTransport } from "./core/email.js";
16
16
  export { issueToken, consumeToken, hashToken, tokenState } from "./core/tokens.js";
17
17
  export { signupPolicy, validateCredentials, validatePassword, PASSWORD_MIN, PASSWORD_MAX } from "./core/signup.js";
18
18
  export { safeNext, safeEqual, isPublic, matchesPattern, isSameOrigin, MATCHER } from "./core/guard.js";
19
+ export { pagePath, pageOverride, appPages, validatePagePath, validateAppPath, landing, sendsHome, PAGE_KEYS } from "./core/pages.js";
19
20
  export { COOKIE_NAME, cookieName, SESSION_DAYS, SIGN_IN_PATH, SIGN_OUT_PATH, SIGN_UP_PATH, getSecret, getDatabaseUrl } from "./core/config.js";