@orangecheck/me-client 0.36.1 → 0.38.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.
package/README.md CHANGED
@@ -90,7 +90,7 @@ export default withOcAuth(handler, {
90
90
  });
91
91
  ```
92
92
 
93
- Or set it in code with `requireBond: { minSats: 100_000, minDays: 30, maxAgeSecs: 3600 }`. ochk.io re-checks the coin when its last check is over ten minutes old; `maxAgeSecs` refuses a claim checked longer ago than you accept. From 0.28.0, an account below the floor gets `403 { ok: false, reason: 'bond_required', need: { min_sats, min_days }, have: { sats_floor, days_floor } | null, back_url }`: link the person to `back_url` to back their account. If the project's setting can't be read and never has been, the request gets `503 { reason: 'bond_policy_unavailable' }` rather than being let through unchecked. `readBondClaim(session)` and `bondMeets(session, { minSats, minDays, maxAgeSecs })` are exported for your own checks.
93
+ Or set it in code with `requireBond: { minSats: 100_000, minDays: 30, maxAgeSecs: 3600 }`. ochk.io re-checks the coin when its last check is over ten minutes old; `maxAgeSecs` refuses a claim checked longer ago than you accept. From 0.28.0, an account below the floor gets `403 { ok: false, reason: 'bond_required', need: { min_sats, min_days }, have: { sats_floor, days_floor } | null, back_url }`: link the person to `back_url` to back their account. If the project's setting can't be read, the last one read stands for ten minutes; with none read in that time, a signed-in request gets `503 { reason: 'bond_policy_unavailable' }` rather than being let through unchecked. A request with no session is not held to the project's setting and never waits on it (0.37.0); pass `required: true` to answer it 401. `readBondClaim(session)` and `bondMeets(session, { minSats, minDays, maxAgeSecs })` are exported for your own checks.
94
94
 
95
95
  ## Pay your users, when you choose to
96
96
 
@@ -249,7 +249,7 @@ app.get('/api/me', (c) => {
249
249
 
250
250
  ### Sessions ended everywhere
251
251
 
252
- Set `OC_PROJECT_KEY` and `OC_SIGNING_SECRET` (from ochk.app/me/projects) and `getOcSession`, `withOcAuth`, `ocAuthExpress` and `ocAuthHono` refuse a session the person ended everywhere: sign out everywhere, a passkey removed, the account deleted. A sign-in token carries `rh`, its revocation handle for your origin; they look it up in me.ochk.io's list of ended handles for that origin, read once a minute per origin with a request signed by your secret, so a refusal lands within about two minutes. The handles name nobody, and another site's handles for the same person never match yours. If the list can't be read, or is more than ten minutes old by its own clock, reads go on and writes are refused until it can. A token without `rh`, and every token while the two variables are unset, is checked by its signature alone and stays valid until it expires. Needs 0.35.0 or later.
252
+ Set `OC_PROJECT_KEY` and `OC_SIGNING_SECRET` (from ochk.app/me/projects) and `getOcSession`, `withOcAuth`, `ocAuthExpress` and `ocAuthHono` refuse a session the person ended everywhere: sign out everywhere, a passkey removed, the account deleted. A sign-in token carries `rh`, its revocation handle for your origin; they look it up in me.ochk.io's list of ended handles for that origin, read once a minute per origin with a request signed by your secret, so a refusal lands within about two minutes. The handles name nobody, and another site's handles for the same person never match yours. If the list can't be read, or is more than ten minutes old by its own clock, reads go on and writes are refused until it can: the adapters answer such a write `503 { reason: 'revocations_unavailable' }` (0.38.0), not 401, so a client retries instead of signing the person out, and `checkOcSession(headers, options)` tells the same apart for your own code (`signed_in`, `signed_out` or `unavailable`). A token without `rh`, and every token while the two variables are unset, is checked by its signature alone and stays valid until it expires. Needs 0.35.0 or later.
253
253
 
254
254
  ## Browser popup signin
255
255
 
package/dist/server.d.mts CHANGED
@@ -10,6 +10,15 @@ interface OcSessionOptions extends VerifyOcOptions {
10
10
  }
11
11
  declare function _resetSessionChecksForTests(): void;
12
12
  declare function getOcSession(headers: SessionRequestHeaders | Headers, options?: OcSessionOptions): Promise<SessionPayload | null>;
13
+ type OcSessionCheck = {
14
+ status: 'signed_in';
15
+ session: SessionPayload;
16
+ } | {
17
+ status: 'signed_out';
18
+ } | {
19
+ status: 'unavailable';
20
+ };
21
+ declare function checkOcSession(headers: SessionRequestHeaders | Headers, options?: OcSessionOptions): Promise<OcSessionCheck>;
13
22
  interface NextApiRequestLike {
14
23
  method?: string;
15
24
  headers: {
@@ -80,4 +89,4 @@ interface HonoContextLike {
80
89
  type HonoNextLike = () => Promise<void>;
81
90
  declare function ocAuthHono(options?: WithOcAuthOptions): (c: HonoContextLike, next: HonoNextLike) => Promise<unknown>;
82
91
 
83
- export { BACK_ACCOUNT_URL, type OcHonoEnv, type OcSessionOptions, type WithOcAuthOptions, type WithOcSession, _resetSessionChecksForTests, getOcSession, ocAuthExpress, ocAuthHono, withOcAuth };
92
+ export { BACK_ACCOUNT_URL, type OcHonoEnv, type OcSessionCheck, type OcSessionOptions, type WithOcAuthOptions, type WithOcSession, _resetSessionChecksForTests, checkOcSession, getOcSession, ocAuthExpress, ocAuthHono, withOcAuth };
package/dist/server.d.ts CHANGED
@@ -10,6 +10,15 @@ interface OcSessionOptions extends VerifyOcOptions {
10
10
  }
11
11
  declare function _resetSessionChecksForTests(): void;
12
12
  declare function getOcSession(headers: SessionRequestHeaders | Headers, options?: OcSessionOptions): Promise<SessionPayload | null>;
13
+ type OcSessionCheck = {
14
+ status: 'signed_in';
15
+ session: SessionPayload;
16
+ } | {
17
+ status: 'signed_out';
18
+ } | {
19
+ status: 'unavailable';
20
+ };
21
+ declare function checkOcSession(headers: SessionRequestHeaders | Headers, options?: OcSessionOptions): Promise<OcSessionCheck>;
13
22
  interface NextApiRequestLike {
14
23
  method?: string;
15
24
  headers: {
@@ -80,4 +89,4 @@ interface HonoContextLike {
80
89
  type HonoNextLike = () => Promise<void>;
81
90
  declare function ocAuthHono(options?: WithOcAuthOptions): (c: HonoContextLike, next: HonoNextLike) => Promise<unknown>;
82
91
 
83
- export { BACK_ACCOUNT_URL, type OcHonoEnv, type OcSessionOptions, type WithOcAuthOptions, type WithOcSession, _resetSessionChecksForTests, getOcSession, ocAuthExpress, ocAuthHono, withOcAuth };
92
+ export { BACK_ACCOUNT_URL, type OcHonoEnv, type OcSessionCheck, type OcSessionOptions, type WithOcAuthOptions, type WithOcSession, _resetSessionChecksForTests, checkOcSession, getOcSession, ocAuthExpress, ocAuthHono, withOcAuth };
package/dist/server.js CHANGED
@@ -600,15 +600,22 @@ function _resetSessionChecksForTests() {
600
600
  warnedUnchecked = false;
601
601
  }
602
602
  async function getOcSession(headers, options = {}) {
603
+ const checked = await checkOcSession(headers, options);
604
+ return checked.status === "signed_in" ? checked.session : null;
605
+ }
606
+ async function checkOcSession(headers, options = {}) {
603
607
  const session = await authCore.getOcSession(headers, options);
604
608
  if (!session) {
605
609
  if (options.audience === void 0) warnIfAudienceMissing(headers);
606
- return null;
610
+ return { status: "signed_out" };
607
611
  }
608
612
  const check = revocationFor(options);
609
- if (check && await check.refuses(session, options.method)) return null;
610
- return session;
613
+ if (check && await check.refuses(session, options.method)) {
614
+ return await check.status(session) === "revoked" ? { status: "signed_out" } : { status: "unavailable" };
615
+ }
616
+ return { status: "signed_in", session };
611
617
  }
618
+ var UNAVAILABLE = { status: 503, body: { ok: false, reason: "revocations_unavailable" } };
612
619
  var warnedAudience = false;
613
620
  function warnIfAudienceMissing(headers) {
614
621
  if (warnedAudience) return;
@@ -690,10 +697,11 @@ async function bondNeed(options) {
690
697
  return null;
691
698
  }
692
699
  async function refusal(session, options) {
693
- const need = await bondNeed(options);
694
700
  if (!session && options.required) {
695
701
  return { status: 401, body: { ok: false, reason: "unauthenticated" } };
696
702
  }
703
+ if (!session && !options.requireBond) return null;
704
+ const need = await bondNeed(options);
697
705
  if (need === UNKNOWN_PROJECT) {
698
706
  return { status: 500, body: { ok: false, reason: "unknown_project_key" } };
699
707
  }
@@ -726,16 +734,17 @@ function withOcAuth(handler, options = {}) {
726
734
  return async (req, res) => {
727
735
  res.setHeader("Cache-Control", "no-store, private");
728
736
  res.setHeader("Vary", "Cookie, Authorization");
729
- const session = await getOcSession(
737
+ const checked = await checkOcSession(
730
738
  {
731
739
  cookie: typeof req.headers.cookie === "string" ? req.headers.cookie : null,
732
740
  authorization: typeof req.headers.authorization === "string" ? req.headers.authorization : null
733
741
  },
734
742
  { ...options, method: req.method }
735
743
  );
744
+ const session = checked.status === "signed_in" ? checked.session : null;
736
745
  const withSession = req;
737
746
  withSession.ocSession = session;
738
- const refused = await refusal(session, options);
747
+ const refused = checked.status === "unavailable" ? UNAVAILABLE : await refusal(session, options);
739
748
  if (refused) return res.status(refused.status).json(refused.body);
740
749
  return handler(withSession, res);
741
750
  };
@@ -745,15 +754,16 @@ function ocAuthExpress(options = {}) {
745
754
  try {
746
755
  res.setHeader("Cache-Control", "no-store, private");
747
756
  res.setHeader("Vary", "Cookie, Authorization");
748
- const session = await getOcSession(
757
+ const checked = await checkOcSession(
749
758
  {
750
759
  cookie: typeof req.headers.cookie === "string" ? req.headers.cookie : null,
751
760
  authorization: typeof req.headers.authorization === "string" ? req.headers.authorization : null
752
761
  },
753
762
  { ...options, method: req.method }
754
763
  );
764
+ const session = checked.status === "signed_in" ? checked.session : null;
755
765
  req.ocSession = session;
756
- const refused = await refusal(session, options);
766
+ const refused = checked.status === "unavailable" ? UNAVAILABLE : await refusal(session, options);
757
767
  if (refused) {
758
768
  res.status(refused.status).json(refused.body);
759
769
  return;
@@ -768,12 +778,13 @@ function ocAuthHono(options = {}) {
768
778
  return async (c, next) => {
769
779
  c.header("Cache-Control", "no-store, private");
770
780
  c.header("Vary", "Cookie, Authorization");
771
- const session = await getOcSession(c.req.raw.headers, {
781
+ const checked = await checkOcSession(c.req.raw.headers, {
772
782
  ...options,
773
783
  method: c.req.raw.method
774
784
  });
785
+ const session = checked.status === "signed_in" ? checked.session : null;
775
786
  c.set("ocSession", session);
776
- const refused = await refusal(session, options);
787
+ const refused = checked.status === "unavailable" ? UNAVAILABLE : await refusal(session, options);
777
788
  if (refused) return c.json(refused.body, refused.status);
778
789
  await next();
779
790
  };
@@ -793,6 +804,7 @@ Object.defineProperty(exports, "verifyOcToken", {
793
804
  });
794
805
  exports.BACK_ACCOUNT_URL = BACK_ACCOUNT_URL;
795
806
  exports._resetSessionChecksForTests = _resetSessionChecksForTests;
807
+ exports.checkOcSession = checkOcSession;
796
808
  exports.getOcSession = getOcSession;
797
809
  exports.identity = identity;
798
810
  exports.ocAuthExpress = ocAuthExpress;