@greenpandastudios/aug-cli 0.19.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/LICENSE +21 -0
- package/README.md +13 -0
- package/THIRD_PARTY_NOTICES.md +18 -0
- package/bin/aug.mjs +3 -0
- package/docs/api/crypto.md +351 -0
- package/docs/api/io.md +207 -0
- package/docs/api/json.md +21 -0
- package/docs/api/memory.md +109 -0
- package/docs/api/time.md +54 -0
- package/docs/api/web.md +184 -0
- package/docs/assets/benchmarks/execution.svg +2978 -0
- package/docs/assets/benchmarks/http.svg +1674 -0
- package/docs/assets/benchmarks/improvements.svg +2315 -0
- package/docs/assets/benchmarks/memory.svg +1891 -0
- package/docs/benchmark-baseline.json +1087 -0
- package/docs/benchmark-results.json +91123 -0
- package/docs/benchmarks.json +38 -0
- package/docs/compatibility.md +42 -0
- package/docs/concurrency-implementation.md +18 -0
- package/docs/diagnostics.md +97 -0
- package/docs/docker.md +35 -0
- package/docs/example-projects.json +26 -0
- package/docs/examples/approved-design/counters.md +253 -0
- package/docs/examples/approved-design/dependencies/august/0.19.0/io/contracts.md +395 -0
- package/docs/examples/approved-design/domain/app.md +165 -0
- package/docs/examples/approved-design/domain/export.md +83 -0
- package/docs/examples/approved-design/domain/models.md +80 -0
- package/docs/examples/approved-design/domain/numbers.md +248 -0
- package/docs/examples/approved-design/index.md +39 -0
- package/docs/examples/approved-design/main-yaml.md +24 -0
- package/docs/examples/approved-design/main.md +215 -0
- package/docs/examples/benchmark/index.md +33 -0
- package/docs/examples/benchmark/main-yaml.md +17 -0
- package/docs/examples/benchmark/main.md +117 -0
- package/docs/examples/cli-args/index.md +32 -0
- package/docs/examples/cli-args/main.md +107 -0
- package/docs/examples/collections/index.md +32 -0
- package/docs/examples/collections/main.md +118 -0
- package/docs/examples/collections-benchmark/index.md +34 -0
- package/docs/examples/collections-benchmark/main.md +115 -0
- package/docs/examples/cpu-benchmark/index.md +34 -0
- package/docs/examples/cpu-benchmark/main.md +90 -0
- package/docs/examples/developer-workflow/calculator.md +357 -0
- package/docs/examples/developer-workflow/dependencies/august/0.19.0/io/contracts.md +395 -0
- package/docs/examples/developer-workflow/index.md +37 -0
- package/docs/examples/developer-workflow/logging/console.md +124 -0
- package/docs/examples/developer-workflow/logging/export.md +70 -0
- package/docs/examples/developer-workflow/logging/logger.md +111 -0
- package/docs/examples/developer-workflow/main.md +172 -0
- package/docs/examples/drop/index.md +33 -0
- package/docs/examples/drop/main.md +85 -0
- package/docs/examples/drop/resource.md +95 -0
- package/docs/examples/errors/errors.md +85 -0
- package/docs/examples/errors/index.md +33 -0
- package/docs/examples/errors/main.md +92 -0
- package/docs/examples/ffi/index.md +33 -0
- package/docs/examples/ffi/main.md +75 -0
- package/docs/examples/ffi/native.md +93 -0
- package/docs/examples/generic-di/dependencies/august/0.19.0/io/contracts.md +395 -0
- package/docs/examples/generic-di/index.md +33 -0
- package/docs/examples/generic-di/main.md +111 -0
- package/docs/examples/generic-di/types.md +180 -0
- package/docs/examples/generics/index.md +33 -0
- package/docs/examples/generics/main.md +120 -0
- package/docs/examples/generics/types.md +189 -0
- package/docs/examples/hello/app/export.md +67 -0
- package/docs/examples/hello/app/greeter.md +190 -0
- package/docs/examples/hello/dependencies/august/0.19.0/io/contracts.md +395 -0
- package/docs/examples/hello/index.md +37 -0
- package/docs/examples/hello/logging/console.md +121 -0
- package/docs/examples/hello/logging/export.md +71 -0
- package/docs/examples/hello/logging/logger.md +120 -0
- package/docs/examples/hello/main.md +115 -0
- package/docs/examples/http-benchmark/index.md +35 -0
- package/docs/examples/http-benchmark/main.md +79 -0
- package/docs/examples/http-benchmark/routes.md +88 -0
- package/docs/examples/index.md +60 -0
- package/docs/examples/interceptors/app.md +250 -0
- package/docs/examples/interceptors/dependencies/august/0.19.0/io/contracts.md +395 -0
- package/docs/examples/interceptors/index.md +35 -0
- package/docs/examples/interceptors/interceptors.md +284 -0
- package/docs/examples/interceptors/logging.md +155 -0
- package/docs/examples/interceptors/main.md +162 -0
- package/docs/examples/json-benchmark/data.md +71 -0
- package/docs/examples/json-benchmark/dependencies/august/0.19.0/json/contracts.md +103 -0
- package/docs/examples/json-benchmark/index.md +35 -0
- package/docs/examples/json-benchmark/main.md +131 -0
- package/docs/examples/new-syntax/console.md +118 -0
- package/docs/examples/new-syntax/dependencies/august/0.19.0/io/contracts.md +395 -0
- package/docs/examples/new-syntax/greeter.md +145 -0
- package/docs/examples/new-syntax/index.md +36 -0
- package/docs/examples/new-syntax/logger.md +111 -0
- package/docs/examples/new-syntax/main.md +135 -0
- package/docs/examples/new-syntax/math.md +79 -0
- package/docs/examples/oidc-login/client/contracts.md +151 -0
- package/docs/examples/oidc-login/client/endpoints.md +263 -0
- package/docs/examples/oidc-login/client/export.md +107 -0
- package/docs/examples/oidc-login/client/login.md +515 -0
- package/docs/examples/oidc-login/client/logout.md +249 -0
- package/docs/examples/oidc-login/client/protocol.md +595 -0
- package/docs/examples/oidc-login/client/session.md +297 -0
- package/docs/examples/oidc-login/client/views.md +144 -0
- package/docs/examples/oidc-login/common/export.md +115 -0
- package/docs/examples/oidc-login/common/headers.md +162 -0
- package/docs/examples/oidc-login/common/keys.md +336 -0
- package/docs/examples/oidc-login/common/settings.md +116 -0
- package/docs/examples/oidc-login/common/views.md +103 -0
- package/docs/examples/oidc-login/dependencies/august/0.19.0/crypto/contracts.md +925 -0
- package/docs/examples/oidc-login/dependencies/august/0.19.0/crypto/jose.md +434 -0
- package/docs/examples/oidc-login/dependencies/august/0.19.0/json/contracts.md +103 -0
- package/docs/examples/oidc-login/dependencies/august/0.19.0/memory/store.md +374 -0
- package/docs/examples/oidc-login/dependencies/august/0.19.0/time/contracts.md +150 -0
- package/docs/examples/oidc-login/dependencies/august/0.19.0/web/contracts.md +532 -0
- package/docs/examples/oidc-login/index.md +57 -0
- package/docs/examples/oidc-login/main-yaml.md +28 -0
- package/docs/examples/oidc-login/main.md +283 -0
- package/docs/examples/oidc-login/provider/authorization.md +457 -0
- package/docs/examples/oidc-login/provider/contracts.md +267 -0
- package/docs/examples/oidc-login/provider/credentials.md +148 -0
- package/docs/examples/oidc-login/provider/discovery.md +215 -0
- package/docs/examples/oidc-login/provider/export.md +131 -0
- package/docs/examples/oidc-login/provider/token.md +364 -0
- package/docs/examples/oidc-login/provider/userinfo.md +228 -0
- package/docs/examples/oidc-login/provider/views.md +137 -0
- package/docs/examples/ownership/counter.md +140 -0
- package/docs/examples/ownership/index.md +33 -0
- package/docs/examples/ownership/main.md +90 -0
- package/docs/examples/ownership-transfer/dependencies/august/0.19.0/io/contracts.md +395 -0
- package/docs/examples/ownership-transfer/index.md +33 -0
- package/docs/examples/ownership-transfer/main.md +125 -0
- package/docs/examples/ownership-transfer/resource.md +150 -0
- package/docs/examples/packages-app/dependencies/packages/@example/aug-math/0.1.0/arithmetic.md +117 -0
- package/docs/examples/packages-app/index.md +34 -0
- package/docs/examples/packages-app/main-yaml.md +18 -0
- package/docs/examples/packages-app/main.md +80 -0
- package/docs/examples/packages-math/aug-package-json.md +24 -0
- package/docs/examples/packages-math/index.md +36 -0
- package/docs/examples/packages-math/package-json.md +25 -0
- package/docs/examples/packages-math/src/arithmetic.md +121 -0
- package/docs/examples/packages-math/src/export.md +63 -0
- package/docs/examples/startup-benchmark/index.md +34 -0
- package/docs/examples/startup-benchmark/main.md +68 -0
- package/docs/examples/visibility/counter.md +142 -0
- package/docs/examples/visibility/index.md +33 -0
- package/docs/examples/visibility/main.md +96 -0
- package/docs/examples.json +47 -0
- package/docs/getting-started.md +5 -0
- package/docs/grammar.md +136 -0
- package/docs/implementation-map.md +106 -0
- package/docs/index.md +46 -0
- package/docs/language-conformance.md +30 -0
- package/docs/language-constructs.md +1461 -0
- package/docs/language-design-audit.md +147 -0
- package/docs/maintaining-docs.md +43 -0
- package/docs/packages.md +184 -0
- package/docs/performance.md +535 -0
- package/docs/production-readiness.md +39 -0
- package/docs/reference.md +361 -0
- package/docs/release-review.md +23 -0
- package/docs/releasing.md +79 -0
- package/docs/roadmap.md +13 -0
- package/docs/specifications.md +86 -0
- package/docs/testing.md +120 -0
- package/docs/tooling.md +197 -0
- package/docs/web-implementation-plan.md +39 -0
- package/docs/web-library-gaps.md +27 -0
- package/docs/web.md +197 -0
- package/examples/approved-design/.aug-spec/august/0.19.0/io/contracts.aug +36 -0
- package/examples/approved-design/.aug-spec/august/0.19.0/io/contracts.aug.md +316 -0
- package/examples/approved-design/.aug-spec/manifest.json +14 -0
- package/examples/approved-design/counters.aug +22 -0
- package/examples/approved-design/counters.aug.md +177 -0
- package/examples/approved-design/domain/app.aug +12 -0
- package/examples/approved-design/domain/app.aug.md +97 -0
- package/examples/approved-design/domain/export.aug +5 -0
- package/examples/approved-design/domain/export.aug.md +25 -0
- package/examples/approved-design/domain/models.aug +2 -0
- package/examples/approved-design/domain/models.aug.md +30 -0
- package/examples/approved-design/domain/numbers.aug +29 -0
- package/examples/approved-design/domain/numbers.aug.md +141 -0
- package/examples/approved-design/main.aug +26 -0
- package/examples/approved-design/main.aug.md +108 -0
- package/examples/approved-design/main.yaml +8 -0
- package/examples/benchmark/.aug-spec/manifest.json +7 -0
- package/examples/benchmark/main.aug +17 -0
- package/examples/benchmark/main.aug.md +43 -0
- package/examples/benchmark/main.yaml +1 -0
- package/examples/cli-args/.aug-spec/manifest.json +7 -0
- package/examples/cli-args/main.aug +15 -0
- package/examples/cli-args/main.aug.md +38 -0
- package/examples/collections/.aug-spec/manifest.json +7 -0
- package/examples/collections/main.aug +18 -0
- package/examples/collections/main.aug.md +43 -0
- package/examples/developer-workflow/.aug-spec/august/0.19.0/io/contracts.aug +36 -0
- package/examples/developer-workflow/.aug-spec/august/0.19.0/io/contracts.aug.md +316 -0
- package/examples/developer-workflow/.aug-spec/manifest.json +13 -0
- package/examples/developer-workflow/calculator.aug +55 -0
- package/examples/developer-workflow/calculator.aug.md +228 -0
- package/examples/developer-workflow/logging/console.aug +8 -0
- package/examples/developer-workflow/logging/console.aug.md +67 -0
- package/examples/developer-workflow/logging/export.aug +2 -0
- package/examples/developer-workflow/logging/export.aug.md +19 -0
- package/examples/developer-workflow/logging/logger.aug +6 -0
- package/examples/developer-workflow/logging/logger.aug.md +57 -0
- package/examples/developer-workflow/main.aug +25 -0
- package/examples/developer-workflow/main.aug.md +79 -0
- package/examples/drop/.aug-spec/manifest.json +8 -0
- package/examples/drop/main.aug +3 -0
- package/examples/drop/main.aug.md +35 -0
- package/examples/drop/resource.aug +8 -0
- package/examples/drop/resource.aug.md +44 -0
- package/examples/errors/.aug-spec/manifest.json +8 -0
- package/examples/errors/errors.aug +6 -0
- package/examples/errors/errors.aug.md +33 -0
- package/examples/errors/main.aug +7 -0
- package/examples/errors/main.aug.md +36 -0
- package/examples/ffi/.aug-spec/manifest.json +8 -0
- package/examples/ffi/main.aug +2 -0
- package/examples/ffi/main.aug.md +27 -0
- package/examples/ffi/native.aug +6 -0
- package/examples/ffi/native.aug.md +43 -0
- package/examples/generic-di/.aug-spec/august/0.19.0/io/contracts.aug +36 -0
- package/examples/generic-di/.aug-spec/august/0.19.0/io/contracts.aug.md +316 -0
- package/examples/generic-di/.aug-spec/manifest.json +10 -0
- package/examples/generic-di/main.aug +9 -0
- package/examples/generic-di/main.aug.md +49 -0
- package/examples/generic-di/types.aug +17 -0
- package/examples/generic-di/types.aug.md +124 -0
- package/examples/generics/.aug-spec/manifest.json +8 -0
- package/examples/generics/main.aug +9 -0
- package/examples/generics/main.aug.md +58 -0
- package/examples/generics/types.aug +19 -0
- package/examples/generics/types.aug.md +132 -0
- package/examples/hello/.aug-spec/august/0.19.0/io/contracts.aug +36 -0
- package/examples/hello/.aug-spec/august/0.19.0/io/contracts.aug.md +316 -0
- package/examples/hello/.aug-spec/manifest.json +14 -0
- package/examples/hello/app/export.aug +1 -0
- package/examples/hello/app/export.aug.md +17 -0
- package/examples/hello/app/greeter.aug +22 -0
- package/examples/hello/app/greeter.aug.md +109 -0
- package/examples/hello/logging/console.aug +7 -0
- package/examples/hello/logging/console.aug.md +65 -0
- package/examples/hello/logging/export.aug +2 -0
- package/examples/hello/logging/export.aug.md +19 -0
- package/examples/hello/logging/logger.aug +9 -0
- package/examples/hello/logging/logger.aug.md +59 -0
- package/examples/hello/main.aug +9 -0
- package/examples/hello/main.aug.md +49 -0
- package/examples/interceptors/.aug-spec/august/0.19.0/io/contracts.aug +36 -0
- package/examples/interceptors/.aug-spec/august/0.19.0/io/contracts.aug.md +316 -0
- package/examples/interceptors/.aug-spec/manifest.json +12 -0
- package/examples/interceptors/app.aug +28 -0
- package/examples/interceptors/app.aug.md +162 -0
- package/examples/interceptors/interceptors.aug +40 -0
- package/examples/interceptors/interceptors.aug.md +180 -0
- package/examples/interceptors/logging.aug +12 -0
- package/examples/interceptors/logging.aug.md +96 -0
- package/examples/interceptors/main.aug +22 -0
- package/examples/interceptors/main.aug.md +76 -0
- package/examples/new-syntax/.aug-spec/august/0.19.0/io/contracts.aug +36 -0
- package/examples/new-syntax/.aug-spec/august/0.19.0/io/contracts.aug.md +316 -0
- package/examples/new-syntax/.aug-spec/manifest.json +13 -0
- package/examples/new-syntax/console.aug +7 -0
- package/examples/new-syntax/console.aug.md +63 -0
- package/examples/new-syntax/greeter.aug +10 -0
- package/examples/new-syntax/greeter.aug.md +89 -0
- package/examples/new-syntax/logger.aug +6 -0
- package/examples/new-syntax/logger.aug.md +57 -0
- package/examples/new-syntax/main.aug +12 -0
- package/examples/new-syntax/main.aug.md +64 -0
- package/examples/new-syntax/math.aug +3 -0
- package/examples/new-syntax/math.aug.md +29 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/crypto/contracts.aug +73 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/crypto/contracts.aug.md +791 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/crypto/jose.aug +68 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/crypto/jose.aug.md +266 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/json/contracts.aug +6 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/json/contracts.aug.md +55 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/memory/store.aug +45 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/memory/store.aug.md +250 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/time/contracts.aug +11 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/time/contracts.aug.md +96 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/web/contracts.aug +58 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/web/contracts.aug.md +420 -0
- package/examples/oidc-login/.aug-spec/manifest.json +40 -0
- package/examples/oidc-login/README.md +29 -0
- package/examples/oidc-login/client/contracts.aug +7 -0
- package/examples/oidc-login/client/contracts.aug.md +80 -0
- package/examples/oidc-login/client/endpoints.aug +21 -0
- package/examples/oidc-login/client/endpoints.aug.md +161 -0
- package/examples/oidc-login/client/export.aug +7 -0
- package/examples/oidc-login/client/export.aug.md +29 -0
- package/examples/oidc-login/client/login.aug +58 -0
- package/examples/oidc-login/client/login.aug.md +331 -0
- package/examples/oidc-login/client/logout.aug +18 -0
- package/examples/oidc-login/client/logout.aug.md +150 -0
- package/examples/oidc-login/client/protocol.aug +106 -0
- package/examples/oidc-login/client/protocol.aug.md +326 -0
- package/examples/oidc-login/client/session.aug +34 -0
- package/examples/oidc-login/client/session.aug.md +155 -0
- package/examples/oidc-login/client/views.aug +21 -0
- package/examples/oidc-login/client/views.aug.md +68 -0
- package/examples/oidc-login/common/export.aug +9 -0
- package/examples/oidc-login/common/export.aug.md +33 -0
- package/examples/oidc-login/common/headers.aug +11 -0
- package/examples/oidc-login/common/headers.aug.md +79 -0
- package/examples/oidc-login/common/keys.aug +37 -0
- package/examples/oidc-login/common/keys.aug.md +207 -0
- package/examples/oidc-login/common/settings.aug +4 -0
- package/examples/oidc-login/common/settings.aug.md +47 -0
- package/examples/oidc-login/common/views.aug +16 -0
- package/examples/oidc-login/common/views.aug.md +34 -0
- package/examples/oidc-login/main.aug +39 -0
- package/examples/oidc-login/main.aug.md +166 -0
- package/examples/oidc-login/main.yaml +12 -0
- package/examples/oidc-login/provider/authorization.aug +59 -0
- package/examples/oidc-login/provider/authorization.aug.md +264 -0
- package/examples/oidc-login/provider/contracts.aug +15 -0
- package/examples/oidc-login/provider/contracts.aug.md +193 -0
- package/examples/oidc-login/provider/credentials.aug +10 -0
- package/examples/oidc-login/provider/credentials.aug.md +64 -0
- package/examples/oidc-login/provider/discovery.aug +13 -0
- package/examples/oidc-login/provider/discovery.aug.md +133 -0
- package/examples/oidc-login/provider/export.aug +13 -0
- package/examples/oidc-login/provider/export.aug.md +41 -0
- package/examples/oidc-login/provider/token.aug +37 -0
- package/examples/oidc-login/provider/token.aug.md +223 -0
- package/examples/oidc-login/provider/userinfo.aug +26 -0
- package/examples/oidc-login/provider/userinfo.aug.md +104 -0
- package/examples/oidc-login/provider/views.aug +18 -0
- package/examples/oidc-login/provider/views.aug.md +63 -0
- package/examples/ownership/.aug-spec/manifest.json +8 -0
- package/examples/ownership/counter.aug +14 -0
- package/examples/ownership/counter.aug.md +85 -0
- package/examples/ownership/main.aug +4 -0
- package/examples/ownership/main.aug.md +38 -0
- package/examples/ownership-transfer/.aug-spec/august/0.19.0/io/contracts.aug +36 -0
- package/examples/ownership-transfer/.aug-spec/august/0.19.0/io/contracts.aug.md +316 -0
- package/examples/ownership-transfer/.aug-spec/manifest.json +10 -0
- package/examples/ownership-transfer/main.aug +9 -0
- package/examples/ownership-transfer/main.aug.md +63 -0
- package/examples/ownership-transfer/resource.aug +16 -0
- package/examples/ownership-transfer/resource.aug.md +89 -0
- package/examples/packages/README.md +15 -0
- package/examples/packages/app/.aug-spec/manifest.json +9 -0
- package/examples/packages/app/.aug-spec/packages/@example/aug-math/0.1.0/arithmetic.aug +9 -0
- package/examples/packages/app/.aug-spec/packages/@example/aug-math/0.1.0/arithmetic.aug.md +63 -0
- package/examples/packages/app/main.aug +2 -0
- package/examples/packages/app/main.aug.md +33 -0
- package/examples/packages/app/main.yaml +2 -0
- package/examples/packages/math/.aug-spec/manifest.json +8 -0
- package/examples/packages/math/README.md +3 -0
- package/examples/packages/math/aug-package.json +8 -0
- package/examples/packages/math/package.json +9 -0
- package/examples/packages/math/src/arithmetic.aug +8 -0
- package/examples/packages/math/src/arithmetic.aug.md +63 -0
- package/examples/packages/math/src/export.aug +1 -0
- package/examples/packages/math/src/export.aug.md +17 -0
- package/examples/visibility/.aug-spec/manifest.json +8 -0
- package/examples/visibility/counter.aug +14 -0
- package/examples/visibility/counter.aug.md +87 -0
- package/examples/visibility/main.aug +7 -0
- package/examples/visibility/main.aug.md +39 -0
- package/package.json +50 -0
- package/runtime/aug_crypto.c +121 -0
- package/runtime/aug_html.c +93 -0
- package/runtime/aug_http.c +829 -0
- package/runtime/aug_json.c +165 -0
- package/runtime/aug_runtime.c +792 -0
- package/runtime/aug_runtime.h +245 -0
- package/runtime/aug_tasks.c +172 -0
- package/runtime/aug_time.c +5 -0
- package/runtime/aug_values.c +80 -0
- package/scripts/bootstrap-native.mjs +121 -0
- package/scripts/native-dependencies.lock.json +65 -0
- package/scripts/native-home.mjs +11 -0
- package/src/actions.js +109 -0
- package/src/ast.js +19 -0
- package/src/builtins.js +93 -0
- package/src/checker.js +2896 -0
- package/src/cli.js +344 -0
- package/src/codegen.js +1159 -0
- package/src/config.js +149 -0
- package/src/continuation.js +110 -0
- package/src/di.js +24 -0
- package/src/documentation.js +27 -0
- package/src/editor.js +782 -0
- package/src/effects.js +29 -0
- package/src/fixes.js +222 -0
- package/src/formatter.js +296 -0
- package/src/freshness.js +154 -0
- package/src/help.js +212 -0
- package/src/html.js +27 -0
- package/src/http-policies.js +90 -0
- package/src/inference.js +16 -0
- package/src/interceptors.js +70 -0
- package/src/javadoc.js +67 -0
- package/src/lexer.js +199 -0
- package/src/libraries.js +31 -0
- package/src/lsp.js +180 -0
- package/src/native.js +144 -0
- package/src/navigation.js +162 -0
- package/src/openapi.js +220 -0
- package/src/ownership.js +232 -0
- package/src/package-manager.js +299 -0
- package/src/parser.js +1155 -0
- package/src/policies.js +130 -0
- package/src/project-init.js +19 -0
- package/src/project.js +292 -0
- package/src/schemas.js +62 -0
- package/src/semantic.js +255 -0
- package/src/spec.js +738 -0
- package/src/testing.js +186 -0
- package/src/types.js +10 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
<!-- Generated by aug spec. Edit the August source, then regenerate. -->
|
|
2
|
+
|
|
3
|
+
# `logout.aug` specification
|
|
4
|
+
|
|
5
|
+
August 0.19.0. This document is compiled from checked code with deterministic wording guided by Simplified Technical English.
|
|
6
|
+
|
|
7
|
+
## In this file
|
|
8
|
+
|
|
9
|
+
- [`logout`](logout.aug.md#symbol-logout) handles `POST` `/logout` returning `HttpResponse<Html>`.
|
|
10
|
+
|
|
11
|
+
<a id="symbol-logout"></a>
|
|
12
|
+
|
|
13
|
+
## `logout`
|
|
14
|
+
|
|
15
|
+
[source](logout.aug#L9)
|
|
16
|
+
|
|
17
|
+
**Inputs**
|
|
18
|
+
|
|
19
|
+
- `input` ([`LogoutForm`](contracts.aug.md#symbol-LogoutForm)) — read from the HTTP form.
|
|
20
|
+
- `token` (`optional string`) — read from the HTTP cookie named `aug_session`; absent value becomes null.
|
|
21
|
+
- `origin` (`optional string`) — read from the HTTP header; absent value becomes null.
|
|
22
|
+
- `crypto` ([`Crypto`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto)) — injected; callers omit it.
|
|
23
|
+
- `clock` ([`Clock`](../.aug-spec/august/0.19.0/time/contracts.aug.md#symbol-Clock)) — injected; callers omit it.
|
|
24
|
+
- `keys` ([`SigningKeys`](../common/keys.aug.md#symbol-SigningKeys)) — injected; callers omit it.
|
|
25
|
+
- `sessions` ([`ExpiringStore<SessionClaims>`](../.aug-spec/august/0.19.0/memory/store.aug.md#symbol-ExpiringStore)) — injected; callers omit it.
|
|
26
|
+
|
|
27
|
+
Returns: `HttpResponse<Html>`.
|
|
28
|
+
|
|
29
|
+
Capabilities: [`crypto.publicRsa`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.publicRsa), [`crypto.decodeBase64url`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.decodeBase64url), [`crypto.verifyRsa`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.verifyRsa), [`crypto.equal`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.equal), [`clock.now`](../.aug-spec/august/0.19.0/time/contracts.aug.md#symbol-Clock.now), [`keys.session`](../common/keys.aug.md#symbol-SigningKeys.session), [`sessions.get`](../.aug-spec/august/0.19.0/memory/store.aug.md#symbol-ExpiringStore.get), [`sessions.take`](../.aug-spec/august/0.19.0/memory/store.aug.md#symbol-ExpiringStore.take).
|
|
30
|
+
|
|
31
|
+
Can fail with `SessionError`, `KeyError`, `TimeError`, `CryptoError`, `HttpError`. Callers must catch or propagate these errors.
|
|
32
|
+
|
|
33
|
+
HTTP route: `POST` `/logout`. Use status 200 when the handler returns a body; a returned HttpResponse can set its own status. An unhandled request failure returns status 500 and cancels its request tasks.
|
|
34
|
+
|
|
35
|
+
Declared HTTP failures: [`SessionError`](contracts.aug.md#symbol-SessionError) returns status 403.
|
|
36
|
+
|
|
37
|
+
**What it does**
|
|
38
|
+
|
|
39
|
+
- Set `config` to call [`settings`](../common/settings.aug.md#symbol-settings).
|
|
40
|
+
- If `origin` does not equal `baseUrl` of `config`:
|
|
41
|
+
- Fail with call [`SessionError`](contracts.aug.md#symbol-SessionError). Transfer control to a matching catch or propagate the failure.
|
|
42
|
+
- Set `session` to call [`authenticate`](session.aug.md#symbol-authenticate) with `token` = `token`; inject `crypto` from `crypto`, `clock` from `clock`, `keys` from `keys`, `sessions` from `sessions`.
|
|
43
|
+
- If not (call [`Crypto.equal`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.equal) on `crypto` with `left` = call `bytes` on `csrf` of `input`; `right` = call `bytes` on `csrf` of `session`):
|
|
44
|
+
- Fail with call [`SessionError`](contracts.aug.md#symbol-SessionError). Transfer control to a matching catch or propagate the failure.
|
|
45
|
+
- Call [`ExpiringStore.take`](../.aug-spec/august/0.19.0/memory/store.aug.md#symbol-ExpiringStore.take) on `sessions` with `key` = `jti` of `session`; `now` = call [`Clock.now`](../.aug-spec/august/0.19.0/time/contracts.aug.md#symbol-Clock.now) on `clock`.
|
|
46
|
+
- Set `headers` to call [`withCookie`](../common/headers.aug.md#symbol-withCookie) with `headers` = call `with` on call [`securityHeaders`](../common/headers.aug.md#symbol-securityHeaders) with `name` = `"location"`; `value` = `"/"`; `name` = `"aug_session"`; `value` = `""`; `path` = `"/"`; `maxAge` = `0`; `secure` = `secureCookies` of `config`.
|
|
47
|
+
- Return call `HttpResponse` with `body` = the HTML element `p` containing `Signed out.` (rendered on the server with embedded text escaped); `status` = `303`; `headers` = `headers`.
|
|
48
|
+
|
|
49
|
+
**Author documentation**
|
|
50
|
+
|
|
51
|
+
POST logout checks the origin and session-bound CSRF value, then removes the live registry entry before clearing the cookie.
|
|
52
|
+
|
|
53
|
+
## Dependencies used by this file
|
|
54
|
+
|
|
55
|
+
Only referenced types and operations appear here. Each name links to its complete specification.
|
|
56
|
+
|
|
57
|
+
### [`Crypto`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto)
|
|
58
|
+
|
|
59
|
+
Capability interface from `august.crypto`.
|
|
60
|
+
|
|
61
|
+
- [`Crypto.decodeBase64url`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.decodeBase64url) (`input`: `string`) → `Bytes`; can fail with `CryptoError`.
|
|
62
|
+
- [`Crypto.equal`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.equal) (`left`: `Bytes`, `right`: `Bytes`) → `bool`.
|
|
63
|
+
- [`Crypto.publicRsa`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.publicRsa) (`key`: `RsaPrivateKey`) → `RsaPublicKey`; can fail with `CryptoError`.
|
|
64
|
+
- [`Crypto.verifyRsa`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.verifyRsa) (`publicKey`: `RsaPublicKey`, `input`: `Bytes`, `signature`: `Bytes`) → `bool`; can fail with `CryptoError`.
|
|
65
|
+
|
|
66
|
+
### [`ExpiringStore`](../.aug-spec/august/0.19.0/memory/store.aug.md#symbol-ExpiringStore)
|
|
67
|
+
|
|
68
|
+
Capability interface from `august.memory`.
|
|
69
|
+
|
|
70
|
+
- [`ExpiringStore.get`](../.aug-spec/august/0.19.0/memory/store.aug.md#symbol-ExpiringStore.get) (`key`: `string`, `now`: `int`) → `optional T`.
|
|
71
|
+
- [`ExpiringStore.take`](../.aug-spec/august/0.19.0/memory/store.aug.md#symbol-ExpiringStore.take) (`key`: `string`, `now`: `int`) → `optional T`.
|
|
72
|
+
|
|
73
|
+
### [`Clock`](../.aug-spec/august/0.19.0/time/contracts.aug.md#symbol-Clock)
|
|
74
|
+
|
|
75
|
+
Capability interface from `august.time`.
|
|
76
|
+
|
|
77
|
+
- [`Clock.now`](../.aug-spec/august/0.19.0/time/contracts.aug.md#symbol-Clock.now) (no caller inputs) → `int`; can fail with `TimeError`.
|
|
78
|
+
|
|
79
|
+
### [`LogoutForm`](contracts.aug.md#symbol-LogoutForm)
|
|
80
|
+
|
|
81
|
+
Record from `contracts`.
|
|
82
|
+
|
|
83
|
+
- Read `csrf` (`string`).
|
|
84
|
+
|
|
85
|
+
### [`SessionClaims`](contracts.aug.md#symbol-SessionClaims)
|
|
86
|
+
|
|
87
|
+
Record from `contracts`.
|
|
88
|
+
|
|
89
|
+
- Read `csrf` (`string`).
|
|
90
|
+
- Read `jti` (`string`).
|
|
91
|
+
|
|
92
|
+
### [`SessionError`](contracts.aug.md#symbol-SessionError)
|
|
93
|
+
|
|
94
|
+
Class from `contracts`.
|
|
95
|
+
|
|
96
|
+
- Construct with no caller inputs → [`SessionError`](contracts.aug.md#symbol-SessionError).
|
|
97
|
+
|
|
98
|
+
### [`authenticate`](session.aug.md#symbol-authenticate)
|
|
99
|
+
|
|
100
|
+
Function from `session`.
|
|
101
|
+
|
|
102
|
+
- [`authenticate`](session.aug.md#symbol-authenticate) (`token`: `optional string`) → [`SessionClaims`](contracts.aug.md#symbol-SessionClaims); inject `crypto`: [`Crypto`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto), `clock`: [`Clock`](../.aug-spec/august/0.19.0/time/contracts.aug.md#symbol-Clock), `keys`: [`SigningKeys`](../common/keys.aug.md#symbol-SigningKeys), `sessions`: [`ExpiringStore<SessionClaims>`](../.aug-spec/august/0.19.0/memory/store.aug.md#symbol-ExpiringStore); uses [`crypto.publicRsa`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.publicRsa), [`crypto.decodeBase64url`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.decodeBase64url), [`crypto.verifyRsa`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.verifyRsa), [`crypto.equal`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.equal), [`clock.now`](../.aug-spec/august/0.19.0/time/contracts.aug.md#symbol-Clock.now), [`keys.session`](../common/keys.aug.md#symbol-SigningKeys.session), [`sessions.get`](../.aug-spec/august/0.19.0/memory/store.aug.md#symbol-ExpiringStore.get); can fail with `SessionError`, `KeyError`, `TimeError`.
|
|
103
|
+
|
|
104
|
+
### [`securityHeaders`](../common/headers.aug.md#symbol-securityHeaders)
|
|
105
|
+
|
|
106
|
+
Function from `common`.
|
|
107
|
+
|
|
108
|
+
- [`securityHeaders`](../common/headers.aug.md#symbol-securityHeaders) (no caller inputs) → `Headers`; can fail with `HttpError`.
|
|
109
|
+
|
|
110
|
+
### [`withCookie`](../common/headers.aug.md#symbol-withCookie)
|
|
111
|
+
|
|
112
|
+
Function from `common`.
|
|
113
|
+
|
|
114
|
+
- [`withCookie`](../common/headers.aug.md#symbol-withCookie) (`headers`: `Headers`, `name`: `string`, `value`: `string`, `path`: `string`, `maxAge`: `int`, `secure`: `bool`) → `Headers`; can fail with `HttpError`.
|
|
115
|
+
|
|
116
|
+
### [`KeyError`](../common/keys.aug.md#symbol-KeyError)
|
|
117
|
+
|
|
118
|
+
Class from `common`.
|
|
119
|
+
|
|
120
|
+
Used as a type or provider.
|
|
121
|
+
|
|
122
|
+
### [`SigningKeys`](../common/keys.aug.md#symbol-SigningKeys)
|
|
123
|
+
|
|
124
|
+
Capability interface from `common`.
|
|
125
|
+
|
|
126
|
+
- [`SigningKeys.session`](../common/keys.aug.md#symbol-SigningKeys.session) (no caller inputs) → `RsaPrivateKey`; can fail with `KeyError`.
|
|
127
|
+
|
|
128
|
+
### [`Settings`](../common/settings.aug.md#symbol-Settings)
|
|
129
|
+
|
|
130
|
+
Record.
|
|
131
|
+
|
|
132
|
+
- Read `baseUrl` (`string`).
|
|
133
|
+
- Read `secureCookies` (`bool`).
|
|
134
|
+
|
|
135
|
+
### [`settings`](../common/settings.aug.md#symbol-settings)
|
|
136
|
+
|
|
137
|
+
Function from `common`.
|
|
138
|
+
|
|
139
|
+
- [`settings`](../common/settings.aug.md#symbol-settings) (no caller inputs) → [`Settings`](../common/settings.aug.md#symbol-Settings).
|
|
140
|
+
|
|
141
|
+
## Built-in operations used by this file
|
|
142
|
+
|
|
143
|
+
- `Headers.with` (`name`: `string`, `value`: `string`) → `Headers`: Return new headers with one additional validated field. Header names ignore case; duplicate values remain separate. Can fail with `HttpError`.
|
|
144
|
+
- `string.bytes` (no inputs) → `Bytes`: Encode this string as immutable UTF-8 bytes.
|
|
145
|
+
|
|
146
|
+
[Full built-in reference](https://greenpandastudios.github.io/augscript/language-constructs).
|
|
147
|
+
|
|
148
|
+
## Shared language rules
|
|
149
|
+
|
|
150
|
+
See the [language reference](https://greenpandastudios.github.io/augscript/reference) for numeric, equality, ownership, and task rules.
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import SessionError from contracts
|
|
2
|
+
import Discovery and IdClaims from provider
|
|
3
|
+
import settings from common
|
|
4
|
+
import HttpClient from august.web
|
|
5
|
+
import parse from august.json
|
|
6
|
+
import Crypto and GnuTlsCrypto and RsaJwks and rsaJwk and signJwt and importJwk and verifyJwt and JwtError from august.crypto
|
|
7
|
+
|
|
8
|
+
/** Accept only a successful JSON response. Redirects remain explicit and are never followed by the transport. */
|
|
9
|
+
responseJson(HttpResponse<Bytes> response) returns Json unless SessionError:
|
|
10
|
+
if response.status != 200:
|
|
11
|
+
throw SessionError()
|
|
12
|
+
match response.headers.get(name="content-type"):
|
|
13
|
+
when null:
|
|
14
|
+
throw SessionError()
|
|
15
|
+
when some contentType:
|
|
16
|
+
if (not contentType.startsWith(prefix="application/json")):
|
|
17
|
+
throw SessionError()
|
|
18
|
+
try:
|
|
19
|
+
return parse(input=response.body.text())
|
|
20
|
+
catch ConversionError error:
|
|
21
|
+
throw SessionError()
|
|
22
|
+
catch JsonError error:
|
|
23
|
+
throw SessionError()
|
|
24
|
+
|
|
25
|
+
/** Discovery is fetched over HTTP. Every advertised URL is checked against the registered issuer before any credential is sent. */
|
|
26
|
+
discover(resolve HttpClient client) returns Discovery uses client.request unless SessionError and HttpError:
|
|
27
|
+
config = settings()
|
|
28
|
+
json = responseJson(response=client.request(method="GET", url=config.issuer + "/.well-known/openid-configuration"))
|
|
29
|
+
try:
|
|
30
|
+
document = json.decode<Discovery>()
|
|
31
|
+
if document.issuer != config.issuer or document.authorization_endpoint != config.issuer + "/authorize" or document.token_endpoint != config.issuer + "/token" or document.jwks_uri != config.issuer + "/jwks" or document.userinfo_endpoint != config.issuer + "/userinfo":
|
|
32
|
+
throw SessionError()
|
|
33
|
+
return document
|
|
34
|
+
catch JsonError error:
|
|
35
|
+
throw SessionError()
|
|
36
|
+
|
|
37
|
+
/** Validate the signed ID token using a public key from this issuer's HTTP JWKS, then validate the registered claims and one-use nonce. */
|
|
38
|
+
validateIdentity(string token, string nonce, int now, RsaJwks jwks, resolve Crypto crypto) returns IdClaims uses crypto.decodeBase64url and crypto.importRsa and crypto.verifyRsa and crypto.equal unless SessionError:
|
|
39
|
+
config = settings()
|
|
40
|
+
if jwks.keys.length() != 1:
|
|
41
|
+
throw SessionError()
|
|
42
|
+
try:
|
|
43
|
+
jwk = jwks.keys.get(index=0)
|
|
44
|
+
if jwk.kid != "provider-1":
|
|
45
|
+
throw SessionError()
|
|
46
|
+
publicKey = importJwk(jwk)
|
|
47
|
+
claims = verifyJwt(token, publicKey, kid="provider-1", tokenType="JWT").decode<IdClaims>()
|
|
48
|
+
if claims.iss != config.issuer or claims.aud != config.clientId or claims.sub.length() == 0 or claims.sub.length() > 255:
|
|
49
|
+
throw SessionError()
|
|
50
|
+
if claims.exp <= now or claims.iat < now - 300 or claims.iat > now + 30 or claims.exp <= claims.iat or claims.exp > now + 330:
|
|
51
|
+
throw SessionError()
|
|
52
|
+
if (not crypto.equal(left=claims.nonce.bytes(), right=nonce.bytes())):
|
|
53
|
+
throw SessionError()
|
|
54
|
+
return claims
|
|
55
|
+
catch JwtError error:
|
|
56
|
+
throw SessionError()
|
|
57
|
+
catch JsonError error:
|
|
58
|
+
throw SessionError()
|
|
59
|
+
catch IndexError error:
|
|
60
|
+
throw SessionError()
|
|
61
|
+
catch CryptoError error:
|
|
62
|
+
throw SessionError()
|
|
63
|
+
|
|
64
|
+
test validateIdentity:
|
|
65
|
+
when signed_identity_claims:
|
|
66
|
+
implement Crypto with GnuTlsCrypto
|
|
67
|
+
resolve Crypto to crypto
|
|
68
|
+
key = crypto.generateRsa()
|
|
69
|
+
publicKey = crypto.publicRsa(key)
|
|
70
|
+
jwks = RsaJwks(keys=[rsaJwk(publicKey, kid="provider-1")])
|
|
71
|
+
config = settings()
|
|
72
|
+
now = 1700000000
|
|
73
|
+
expectedNonce = "nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"
|
|
74
|
+
|
|
75
|
+
it accepts_valid_identity:
|
|
76
|
+
claims = IdClaims(iss=config.issuer, sub="ada", aud=config.clientId, exp=now+300, iat=now, nonce=expectedNonce, name="Ada")
|
|
77
|
+
token = signJwt(key, claims=Json(value=claims), kid="provider-1", tokenType="JWT")
|
|
78
|
+
identity = validateIdentity(token, nonce=expectedNonce, now, jwks)
|
|
79
|
+
assert(condition=identity.sub == "ada")
|
|
80
|
+
|
|
81
|
+
it rejects_signed_invalid_claims for (issuer, audience, subject, issued, expires, nonce) in [
|
|
82
|
+
("https://wrong-issuer.invalid", config.clientId, "ada", now, now+300, expectedNonce),
|
|
83
|
+
(config.issuer, "wrong-audience", "ada", now, now+300, expectedNonce),
|
|
84
|
+
(config.issuer, config.clientId, "", now, now+300, expectedNonce),
|
|
85
|
+
(config.issuer, config.clientId, "ada", now-400, now+300, expectedNonce),
|
|
86
|
+
(config.issuer, config.clientId, "ada", now+100, now+300, expectedNonce),
|
|
87
|
+
(config.issuer, config.clientId, "ada", now, now, expectedNonce),
|
|
88
|
+
(config.issuer, config.clientId, "ada", now, now+600, expectedNonce),
|
|
89
|
+
(config.issuer, config.clientId, "ada", now, now+300, "wrong-nonce")
|
|
90
|
+
]:
|
|
91
|
+
claims = IdClaims(iss=issuer, sub=subject, aud=audience, exp=expires, iat=issued, nonce, name="Ada")
|
|
92
|
+
token = signJwt(key, claims=Json(value=claims), kid="provider-1", tokenType="JWT")
|
|
93
|
+
try:
|
|
94
|
+
validateIdentity(token, nonce=expectedNonce, now, jwks)
|
|
95
|
+
assert(condition=false)
|
|
96
|
+
catch SessionError error:
|
|
97
|
+
assert(condition=true)
|
|
98
|
+
|
|
99
|
+
it rejects_token_context for (kid, tokenType) in [("wrong-key", "JWT"), ("provider-1", "august-session+jwt")]:
|
|
100
|
+
claims = IdClaims(iss=config.issuer, sub="ada", aud=config.clientId, exp=now+300, iat=now, nonce=expectedNonce, name="Ada")
|
|
101
|
+
token = signJwt(key, claims=Json(value=claims), kid, tokenType)
|
|
102
|
+
try:
|
|
103
|
+
validateIdentity(token, nonce=expectedNonce, now, jwks)
|
|
104
|
+
assert(condition=false)
|
|
105
|
+
catch SessionError error:
|
|
106
|
+
assert(condition=true)
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
<!-- Generated by aug spec. Edit the August source, then regenerate. -->
|
|
2
|
+
|
|
3
|
+
# `protocol.aug` specification
|
|
4
|
+
|
|
5
|
+
August 0.19.0. This document is compiled from checked code with deterministic wording guided by Simplified Technical English.
|
|
6
|
+
|
|
7
|
+
## In this file
|
|
8
|
+
|
|
9
|
+
- [`responseJson`](protocol.aug.md#symbol-responseJson) is a function returning `Json`.
|
|
10
|
+
- [`discover`](protocol.aug.md#symbol-discover) is a function returning `Discovery`.
|
|
11
|
+
- [`validateIdentity`](protocol.aug.md#symbol-validateIdentity) is a function returning `IdClaims`.
|
|
12
|
+
- [`test validateIdentity`](protocol.aug.md#symbol-test%20validateIdentity) is a same-file test suite.
|
|
13
|
+
|
|
14
|
+
<a id="symbol-responseJson"></a>
|
|
15
|
+
|
|
16
|
+
## `responseJson`
|
|
17
|
+
|
|
18
|
+
[source](protocol.aug#L9)
|
|
19
|
+
|
|
20
|
+
**Inputs**
|
|
21
|
+
|
|
22
|
+
- `response` (`HttpResponse<Bytes>`) — required labeled input.
|
|
23
|
+
|
|
24
|
+
Returns: `Json`.
|
|
25
|
+
|
|
26
|
+
Can fail with `SessionError`. Callers must catch or propagate these errors.
|
|
27
|
+
|
|
28
|
+
**What it does**
|
|
29
|
+
|
|
30
|
+
- If `status` of `response` does not equal `200`:
|
|
31
|
+
- Fail with call [`SessionError`](contracts.aug.md#symbol-SessionError). Transfer control to a matching catch or propagate the failure.
|
|
32
|
+
- Select the matching case for call `get` on `headers` of `response` with `name` = `"content-type"`:
|
|
33
|
+
- A null value, including omitted optional input:
|
|
34
|
+
- Fail with call [`SessionError`](contracts.aug.md#symbol-SessionError). Transfer control to a matching catch or propagate the failure.
|
|
35
|
+
- A present, non-null value, named `contentType`:
|
|
36
|
+
- If not (call `startsWith` on `contentType` with `prefix` = `"application/json"`):
|
|
37
|
+
- Fail with call [`SessionError`](contracts.aug.md#symbol-SessionError). Transfer control to a matching catch or propagate the failure.
|
|
38
|
+
- Try these operations:
|
|
39
|
+
- Return call [`parse`](../.aug-spec/august/0.19.0/json/contracts.aug.md#symbol-parse) with `input` = call `text` on `body` of `response`.
|
|
40
|
+
- If they fail with `ConversionError`, name the failure `error` and recover:
|
|
41
|
+
- Fail with call [`SessionError`](contracts.aug.md#symbol-SessionError). Transfer control to a matching catch or propagate the failure.
|
|
42
|
+
- If they fail with `JsonError`, name the failure `error` and recover:
|
|
43
|
+
- Fail with call [`SessionError`](contracts.aug.md#symbol-SessionError). Transfer control to a matching catch or propagate the failure.
|
|
44
|
+
|
|
45
|
+
**Author documentation**
|
|
46
|
+
|
|
47
|
+
Accept only a successful JSON response. Redirects remain explicit and are never followed by the transport.
|
|
48
|
+
|
|
49
|
+
<a id="symbol-discover"></a>
|
|
50
|
+
|
|
51
|
+
## `discover`
|
|
52
|
+
|
|
53
|
+
[source](protocol.aug#L26)
|
|
54
|
+
|
|
55
|
+
**Inputs**
|
|
56
|
+
|
|
57
|
+
- `client` ([`HttpClient`](../.aug-spec/august/0.19.0/web/contracts.aug.md#symbol-HttpClient)) — injected; callers omit it.
|
|
58
|
+
|
|
59
|
+
Returns: [`Discovery`](../provider/discovery.aug.md#symbol-Discovery).
|
|
60
|
+
|
|
61
|
+
Capabilities: [`client.request`](../.aug-spec/august/0.19.0/web/contracts.aug.md#symbol-HttpClient.request).
|
|
62
|
+
|
|
63
|
+
Can fail with `SessionError`, `HttpError`. Callers must catch or propagate these errors.
|
|
64
|
+
|
|
65
|
+
**What it does**
|
|
66
|
+
|
|
67
|
+
- Set `config` to call [`settings`](../common/settings.aug.md#symbol-settings).
|
|
68
|
+
- Set `json` to call [`responseJson`](protocol.aug.md#symbol-responseJson) with `response` = call [`HttpClient.request`](../.aug-spec/august/0.19.0/web/contracts.aug.md#symbol-HttpClient.request) on `client` with `method` = `"GET"`; `url` = (`issuer` of `config` plus `"/.well-known/openid-configuration"`).
|
|
69
|
+
- Try these operations:
|
|
70
|
+
- Set `document` to call `decode` on `json` with type arguments [`Discovery`](../provider/discovery.aug.md#symbol-Discovery).
|
|
71
|
+
- If ((((`issuer` of `document` does not equal `issuer` of `config`) or (`authorization_endpoint` of `document` does not equal (`issuer` of `config` plus `"/authorize"`))) or (`token_endpoint` of `document` does not equal (`issuer` of `config` plus `"/token"`))) or (`jwks_uri` of `document` does not equal (`issuer` of `config` plus `"/jwks"`))) or (`userinfo_endpoint` of `document` does not equal (`issuer` of `config` plus `"/userinfo"`)):
|
|
72
|
+
- Fail with call [`SessionError`](contracts.aug.md#symbol-SessionError). Transfer control to a matching catch or propagate the failure.
|
|
73
|
+
- Return `document`.
|
|
74
|
+
- If they fail with `JsonError`, name the failure `error` and recover:
|
|
75
|
+
- Fail with call [`SessionError`](contracts.aug.md#symbol-SessionError). Transfer control to a matching catch or propagate the failure.
|
|
76
|
+
|
|
77
|
+
**Author documentation**
|
|
78
|
+
|
|
79
|
+
Discovery is fetched over HTTP. Every advertised URL is checked against the registered issuer before any credential is sent.
|
|
80
|
+
|
|
81
|
+
<a id="symbol-validateIdentity"></a>
|
|
82
|
+
|
|
83
|
+
## `validateIdentity`
|
|
84
|
+
|
|
85
|
+
[source](protocol.aug#L38)
|
|
86
|
+
|
|
87
|
+
**Inputs**
|
|
88
|
+
|
|
89
|
+
- `token` (`string`) — required labeled input.
|
|
90
|
+
- `nonce` (`string`) — required labeled input.
|
|
91
|
+
- `now` (`int`) — required labeled input.
|
|
92
|
+
- `jwks` ([`RsaJwks`](../.aug-spec/august/0.19.0/crypto/jose.aug.md#symbol-RsaJwks)) — required labeled input.
|
|
93
|
+
- `crypto` ([`Crypto`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto)) — injected; callers omit it.
|
|
94
|
+
|
|
95
|
+
Returns: [`IdClaims`](../provider/contracts.aug.md#symbol-IdClaims).
|
|
96
|
+
|
|
97
|
+
Capabilities: [`crypto.decodeBase64url`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.decodeBase64url), [`crypto.importRsa`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.importRsa), [`crypto.verifyRsa`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.verifyRsa), [`crypto.equal`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.equal).
|
|
98
|
+
|
|
99
|
+
Can fail with `SessionError`. Callers must catch or propagate these errors.
|
|
100
|
+
|
|
101
|
+
**What it does**
|
|
102
|
+
|
|
103
|
+
- Set `config` to call [`settings`](../common/settings.aug.md#symbol-settings).
|
|
104
|
+
- If call `length` on `keys` of `jwks` does not equal `1`:
|
|
105
|
+
- Fail with call [`SessionError`](contracts.aug.md#symbol-SessionError). Transfer control to a matching catch or propagate the failure.
|
|
106
|
+
- Try these operations:
|
|
107
|
+
- Set `jwk` to call `get` on `keys` of `jwks` with `index` = `0`.
|
|
108
|
+
- If `kid` of `jwk` does not equal `"provider-1"`:
|
|
109
|
+
- Fail with call [`SessionError`](contracts.aug.md#symbol-SessionError). Transfer control to a matching catch or propagate the failure.
|
|
110
|
+
- Set `publicKey` to call [`importJwk`](../.aug-spec/august/0.19.0/crypto/jose.aug.md#symbol-importJwk) with `jwk` = `jwk`; inject `crypto` from `crypto`.
|
|
111
|
+
- Set `claims` to call `decode` on call [`verifyJwt`](../.aug-spec/august/0.19.0/crypto/jose.aug.md#symbol-verifyJwt) with `token` = `token`; `publicKey` = `publicKey`; `kid` = `"provider-1"`; `tokenType` = `"JWT"`; inject `crypto` from `crypto` with type arguments [`IdClaims`](../provider/contracts.aug.md#symbol-IdClaims).
|
|
112
|
+
- If (((`iss` of `claims` does not equal `issuer` of `config`) or (`aud` of `claims` does not equal `clientId` of `config`)) or (call `length` on `sub` of `claims` equals `0`)) or (call `length` on `sub` of `claims` is greater than `255`):
|
|
113
|
+
- Fail with call [`SessionError`](contracts.aug.md#symbol-SessionError). Transfer control to a matching catch or propagate the failure.
|
|
114
|
+
- If ((((`exp` of `claims` is at most `now`) or (`iat` of `claims` is less than (`now` minus `300`))) or (`iat` of `claims` is greater than (`now` plus `30`))) or (`exp` of `claims` is at most `iat` of `claims`)) or (`exp` of `claims` is greater than (`now` plus `330`)):
|
|
115
|
+
- Fail with call [`SessionError`](contracts.aug.md#symbol-SessionError). Transfer control to a matching catch or propagate the failure.
|
|
116
|
+
- If not (call [`Crypto.equal`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.equal) on `crypto` with `left` = call `bytes` on `nonce` of `claims`; `right` = call `bytes` on `nonce`):
|
|
117
|
+
- Fail with call [`SessionError`](contracts.aug.md#symbol-SessionError). Transfer control to a matching catch or propagate the failure.
|
|
118
|
+
- Return `claims`.
|
|
119
|
+
- If they fail with [`JwtError`](../.aug-spec/august/0.19.0/crypto/jose.aug.md#symbol-JwtError), name the failure `error` and recover:
|
|
120
|
+
- Fail with call [`SessionError`](contracts.aug.md#symbol-SessionError). Transfer control to a matching catch or propagate the failure.
|
|
121
|
+
- If they fail with `JsonError`, name the failure `error` and recover:
|
|
122
|
+
- Fail with call [`SessionError`](contracts.aug.md#symbol-SessionError). Transfer control to a matching catch or propagate the failure.
|
|
123
|
+
- If they fail with `IndexError`, name the failure `error` and recover:
|
|
124
|
+
- Fail with call [`SessionError`](contracts.aug.md#symbol-SessionError). Transfer control to a matching catch or propagate the failure.
|
|
125
|
+
- If they fail with `CryptoError`, name the failure `error` and recover:
|
|
126
|
+
- Fail with call [`SessionError`](contracts.aug.md#symbol-SessionError). Transfer control to a matching catch or propagate the failure.
|
|
127
|
+
|
|
128
|
+
**Author documentation**
|
|
129
|
+
|
|
130
|
+
Validate the signed ID token using a public key from this issuer's HTTP JWKS, then validate the registered claims and one-use nonce.
|
|
131
|
+
|
|
132
|
+
<a id="symbol-test validateIdentity"></a>
|
|
133
|
+
|
|
134
|
+
## `test validateIdentity`
|
|
135
|
+
|
|
136
|
+
[source](protocol.aug#L64)
|
|
137
|
+
|
|
138
|
+
Same-file function tests for [`validateIdentity`](protocol.aug.md#symbol-validateIdentity). Each case gets isolated setup and dependency bindings.
|
|
139
|
+
|
|
140
|
+
### Group `signed_identity_claims`
|
|
141
|
+
|
|
142
|
+
**Setup before each case**
|
|
143
|
+
|
|
144
|
+
- Provide [`GnuTlsCrypto`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-GnuTlsCrypto) when `Crypto` is requested. Use a fresh instance when this provider retains state; otherwise reuse one instance.
|
|
145
|
+
- Set `crypto` to the instance provided for `Crypto`.
|
|
146
|
+
- Set `key` to call [`Crypto.generateRsa`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.generateRsa) on `crypto`.
|
|
147
|
+
- Set `publicKey` to call [`Crypto.publicRsa`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.publicRsa) on `crypto` with `key` = `key`.
|
|
148
|
+
- Set `jwks` to call [`RsaJwks`](../.aug-spec/august/0.19.0/crypto/jose.aug.md#symbol-RsaJwks) with `keys` = a list containing call [`rsaJwk`](../.aug-spec/august/0.19.0/crypto/jose.aug.md#symbol-rsaJwk) with `publicKey` = `publicKey`; `kid` = `"provider-1"`; inject `crypto` from `Crypto`.
|
|
149
|
+
- Set `config` to call [`settings`](../common/settings.aug.md#symbol-settings).
|
|
150
|
+
- Set `now` to `1700000000`.
|
|
151
|
+
- Set `expectedNonce` to `"nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"`.
|
|
152
|
+
|
|
153
|
+
#### `accepts_valid_identity`
|
|
154
|
+
|
|
155
|
+
[source](protocol.aug#L75)
|
|
156
|
+
|
|
157
|
+
- Set `claims` to call [`IdClaims`](../provider/contracts.aug.md#symbol-IdClaims) with `iss` = `issuer` of `config`; `sub` = `"ada"`; `aud` = `clientId` of `config`; `exp` = (`now` plus `300`); `iat` = `now`; `nonce` = `expectedNonce`; `name` = `"Ada"`.
|
|
158
|
+
- Set `token` to call [`signJwt`](../.aug-spec/august/0.19.0/crypto/jose.aug.md#symbol-signJwt) with `key` = `key`; `claims` = call `Json` with `value` = `claims`; `kid` = `"provider-1"`; `tokenType` = `"JWT"`; inject `crypto` from `Crypto`.
|
|
159
|
+
- Set `identity` to call [`validateIdentity`](protocol.aug.md#symbol-validateIdentity) with `token` = `token`; `nonce` = `expectedNonce`; `now` = `now`; `jwks` = `jwks`; inject `crypto` from `Crypto`.
|
|
160
|
+
- Call `assert` with `condition` = (`sub` of `identity` equals `"ada"`).
|
|
161
|
+
|
|
162
|
+
#### `rejects_signed_invalid_claims`
|
|
163
|
+
|
|
164
|
+
[source](protocol.aug#L81)
|
|
165
|
+
|
|
166
|
+
Run once for each row of a tuple containing `"https://wrong-issuer.invalid"`, `clientId` of `config`, `"ada"`, `now`, `now` plus `300`, `expectedNonce`; a tuple containing `issuer` of `config`, `"wrong-audience"`, `"ada"`, `now`, `now` plus `300`, `expectedNonce`; a tuple containing `issuer` of `config`, `clientId` of `config`, `""`, `now`, `now` plus `300`, `expectedNonce`; a tuple containing `issuer` of `config`, `clientId` of `config`, `"ada"`, `now` minus `400`, `now` plus `300`, `expectedNonce`; a tuple containing `issuer` of `config`, `clientId` of `config`, `"ada"`, `now` plus `100`, `now` plus `300`, `expectedNonce`; a tuple containing `issuer` of `config`, `clientId` of `config`, `"ada"`, `now`, `now`, `expectedNonce`; a tuple containing `issuer` of `config`, `clientId` of `config`, `"ada"`, `now`, `now` plus `600`, `expectedNonce`; a tuple containing `issuer` of `config`, `clientId` of `config`, `"ada"`, `now`, `now` plus `300`, `"wrong-nonce"`. Bind row positions to `issuer`, `audience`, `subject`, `issued`, `expires`, `nonce`.
|
|
167
|
+
|
|
168
|
+
- Set `claims` to call [`IdClaims`](../provider/contracts.aug.md#symbol-IdClaims) with `iss` = `issuer`; `sub` = `subject`; `aud` = `audience`; `exp` = `expires`; `iat` = `issued`; `nonce` = `nonce`; `name` = `"Ada"`.
|
|
169
|
+
- Set `token` to call [`signJwt`](../.aug-spec/august/0.19.0/crypto/jose.aug.md#symbol-signJwt) with `key` = `key`; `claims` = call `Json` with `value` = `claims`; `kid` = `"provider-1"`; `tokenType` = `"JWT"`; inject `crypto` from `Crypto`.
|
|
170
|
+
- Try these operations:
|
|
171
|
+
- Call [`validateIdentity`](protocol.aug.md#symbol-validateIdentity) with `token` = `token`; `nonce` = `expectedNonce`; `now` = `now`; `jwks` = `jwks`; inject `crypto` from `Crypto`.
|
|
172
|
+
- Call `assert` with `condition` = `false`.
|
|
173
|
+
- If they fail with [`SessionError`](contracts.aug.md#symbol-SessionError), name the failure `error` and recover:
|
|
174
|
+
- Call `assert` with `condition` = `true`.
|
|
175
|
+
|
|
176
|
+
#### `rejects_token_context`
|
|
177
|
+
|
|
178
|
+
[source](protocol.aug#L99)
|
|
179
|
+
|
|
180
|
+
Run once for each row of a tuple containing `"wrong-key"`, `"JWT"`; a tuple containing `"provider-1"`, `"august-session+jwt"`. Bind row positions to `kid`, `tokenType`.
|
|
181
|
+
|
|
182
|
+
- Set `claims` to call [`IdClaims`](../provider/contracts.aug.md#symbol-IdClaims) with `iss` = `issuer` of `config`; `sub` = `"ada"`; `aud` = `clientId` of `config`; `exp` = (`now` plus `300`); `iat` = `now`; `nonce` = `expectedNonce`; `name` = `"Ada"`.
|
|
183
|
+
- Set `token` to call [`signJwt`](../.aug-spec/august/0.19.0/crypto/jose.aug.md#symbol-signJwt) with `key` = `key`; `claims` = call `Json` with `value` = `claims`; `kid` = `kid`; `tokenType` = `tokenType`; inject `crypto` from `Crypto`.
|
|
184
|
+
- Try these operations:
|
|
185
|
+
- Call [`validateIdentity`](protocol.aug.md#symbol-validateIdentity) with `token` = `token`; `nonce` = `expectedNonce`; `now` = `now`; `jwks` = `jwks`; inject `crypto` from `Crypto`.
|
|
186
|
+
- Call `assert` with `condition` = `false`.
|
|
187
|
+
- If they fail with [`SessionError`](contracts.aug.md#symbol-SessionError), name the failure `error` and recover:
|
|
188
|
+
- Call `assert` with `condition` = `true`.
|
|
189
|
+
|
|
190
|
+
## Dependencies used by this file
|
|
191
|
+
|
|
192
|
+
Only referenced types and operations appear here. Each name links to its complete specification.
|
|
193
|
+
|
|
194
|
+
### [`Crypto`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto)
|
|
195
|
+
|
|
196
|
+
Capability interface from `august.crypto`.
|
|
197
|
+
|
|
198
|
+
- [`Crypto.decodeBase64url`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.decodeBase64url) (`input`: `string`) → `Bytes`; can fail with `CryptoError`.
|
|
199
|
+
- [`Crypto.equal`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.equal) (`left`: `Bytes`, `right`: `Bytes`) → `bool`.
|
|
200
|
+
- [`Crypto.generateRsa`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.generateRsa) (no caller inputs) → `RsaPrivateKey`; can fail with `CryptoError`.
|
|
201
|
+
- [`Crypto.importRsa`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.importRsa) (`modulus`: `Bytes`, `exponent`: `Bytes`) → `RsaPublicKey`; can fail with `CryptoError`.
|
|
202
|
+
- [`Crypto.publicRsa`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.publicRsa) (`key`: `RsaPrivateKey`) → `RsaPublicKey`; can fail with `CryptoError`.
|
|
203
|
+
- [`Crypto.verifyRsa`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.verifyRsa) (`publicKey`: `RsaPublicKey`, `input`: `Bytes`, `signature`: `Bytes`) → `bool`; can fail with `CryptoError`.
|
|
204
|
+
|
|
205
|
+
### [`GnuTlsCrypto`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-GnuTlsCrypto)
|
|
206
|
+
|
|
207
|
+
Class from `august.crypto`.
|
|
208
|
+
|
|
209
|
+
Used as a type or provider.
|
|
210
|
+
|
|
211
|
+
### [`JwtError`](../.aug-spec/august/0.19.0/crypto/jose.aug.md#symbol-JwtError)
|
|
212
|
+
|
|
213
|
+
Class from `august.crypto`.
|
|
214
|
+
|
|
215
|
+
Used as a type or provider.
|
|
216
|
+
|
|
217
|
+
### [`RsaJwk`](../.aug-spec/august/0.19.0/crypto/jose.aug.md#symbol-RsaJwk)
|
|
218
|
+
|
|
219
|
+
Record.
|
|
220
|
+
|
|
221
|
+
- Read `kid` (`string`).
|
|
222
|
+
|
|
223
|
+
### [`RsaJwks`](../.aug-spec/august/0.19.0/crypto/jose.aug.md#symbol-RsaJwks)
|
|
224
|
+
|
|
225
|
+
Record from `august.crypto`.
|
|
226
|
+
|
|
227
|
+
- Construct with `keys`: `List<RsaJwk>` → [`RsaJwks`](../.aug-spec/august/0.19.0/crypto/jose.aug.md#symbol-RsaJwks).
|
|
228
|
+
- Read `keys` (`List<RsaJwk>`).
|
|
229
|
+
|
|
230
|
+
### [`importJwk`](../.aug-spec/august/0.19.0/crypto/jose.aug.md#symbol-importJwk)
|
|
231
|
+
|
|
232
|
+
Function from `august.crypto`.
|
|
233
|
+
|
|
234
|
+
- [`importJwk`](../.aug-spec/august/0.19.0/crypto/jose.aug.md#symbol-importJwk) (`jwk`: [`RsaJwk`](../.aug-spec/august/0.19.0/crypto/jose.aug.md#symbol-RsaJwk)) → `RsaPublicKey`; inject `crypto`: [`Crypto`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto); uses [`crypto.decodeBase64url`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.decodeBase64url), [`crypto.importRsa`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.importRsa); can fail with `JwtError`.
|
|
235
|
+
|
|
236
|
+
### [`rsaJwk`](../.aug-spec/august/0.19.0/crypto/jose.aug.md#symbol-rsaJwk)
|
|
237
|
+
|
|
238
|
+
Function from `august.crypto`.
|
|
239
|
+
|
|
240
|
+
- [`rsaJwk`](../.aug-spec/august/0.19.0/crypto/jose.aug.md#symbol-rsaJwk) (`publicKey`: `RsaPublicKey`, `kid`: `string`) → [`RsaJwk`](../.aug-spec/august/0.19.0/crypto/jose.aug.md#symbol-RsaJwk); inject `crypto`: [`Crypto`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto); uses [`crypto.exportRsa`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.exportRsa); can fail with `CryptoError`.
|
|
241
|
+
|
|
242
|
+
### [`signJwt`](../.aug-spec/august/0.19.0/crypto/jose.aug.md#symbol-signJwt)
|
|
243
|
+
|
|
244
|
+
Function from `august.crypto`.
|
|
245
|
+
|
|
246
|
+
- [`signJwt`](../.aug-spec/august/0.19.0/crypto/jose.aug.md#symbol-signJwt) (`key`: `RsaPrivateKey`, `claims`: `Json`, `kid`: `string`, `tokenType`: `string`) → `string`; inject `crypto`: [`Crypto`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto); uses [`crypto.signRsa`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.signRsa); can fail with `JwtError`.
|
|
247
|
+
|
|
248
|
+
### [`verifyJwt`](../.aug-spec/august/0.19.0/crypto/jose.aug.md#symbol-verifyJwt)
|
|
249
|
+
|
|
250
|
+
Function from `august.crypto`.
|
|
251
|
+
|
|
252
|
+
- [`verifyJwt`](../.aug-spec/august/0.19.0/crypto/jose.aug.md#symbol-verifyJwt) (`token`: `string`, `publicKey`: `RsaPublicKey`, `kid`: `string`, `tokenType`: `string`) → `Json`; inject `crypto`: [`Crypto`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto); uses [`crypto.decodeBase64url`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.decodeBase64url), [`crypto.verifyRsa`](../.aug-spec/august/0.19.0/crypto/contracts.aug.md#symbol-Crypto.verifyRsa); can fail with `JwtError`.
|
|
253
|
+
|
|
254
|
+
### [`parse`](../.aug-spec/august/0.19.0/json/contracts.aug.md#symbol-parse)
|
|
255
|
+
|
|
256
|
+
Function from `august.json`.
|
|
257
|
+
|
|
258
|
+
- [`parse`](../.aug-spec/august/0.19.0/json/contracts.aug.md#symbol-parse) (`input`: `string`) → `Json`; can fail with `JsonError`.
|
|
259
|
+
|
|
260
|
+
### [`HttpClient`](../.aug-spec/august/0.19.0/web/contracts.aug.md#symbol-HttpClient)
|
|
261
|
+
|
|
262
|
+
Capability interface from `august.web`.
|
|
263
|
+
|
|
264
|
+
- [`HttpClient.request`](../.aug-spec/august/0.19.0/web/contracts.aug.md#symbol-HttpClient.request) (`method`: `string`, `url`: `string`, `headers`: `optional Headers`, `body`: `optional Bytes`) → `HttpResponse<Bytes>`; can fail with `HttpError`.
|
|
265
|
+
|
|
266
|
+
### [`SessionError`](contracts.aug.md#symbol-SessionError)
|
|
267
|
+
|
|
268
|
+
Class from `contracts`.
|
|
269
|
+
|
|
270
|
+
- Construct with no caller inputs → [`SessionError`](contracts.aug.md#symbol-SessionError).
|
|
271
|
+
|
|
272
|
+
### [`Settings`](../common/settings.aug.md#symbol-Settings)
|
|
273
|
+
|
|
274
|
+
Record.
|
|
275
|
+
|
|
276
|
+
- Read `clientId` (`string`).
|
|
277
|
+
- Read `issuer` (`string`).
|
|
278
|
+
|
|
279
|
+
### [`settings`](../common/settings.aug.md#symbol-settings)
|
|
280
|
+
|
|
281
|
+
Function from `common`.
|
|
282
|
+
|
|
283
|
+
- [`settings`](../common/settings.aug.md#symbol-settings) (no caller inputs) → [`Settings`](../common/settings.aug.md#symbol-Settings).
|
|
284
|
+
|
|
285
|
+
### [`IdClaims`](../provider/contracts.aug.md#symbol-IdClaims)
|
|
286
|
+
|
|
287
|
+
Record from `provider`.
|
|
288
|
+
|
|
289
|
+
- Construct with `iss`: `string`, `sub`: `string`, `aud`: `string`, `exp`: `int`, `iat`: `int`, `nonce`: `string`, `name`: `string` → [`IdClaims`](../provider/contracts.aug.md#symbol-IdClaims).
|
|
290
|
+
- Read `aud` (`string`).
|
|
291
|
+
- Read `exp` (`int`).
|
|
292
|
+
- Read `iat` (`int`).
|
|
293
|
+
- Read `iss` (`string`).
|
|
294
|
+
- Read `nonce` (`string`).
|
|
295
|
+
- Read `sub` (`string`).
|
|
296
|
+
|
|
297
|
+
### [`Discovery`](../provider/discovery.aug.md#symbol-Discovery)
|
|
298
|
+
|
|
299
|
+
Record from `provider`.
|
|
300
|
+
|
|
301
|
+
- Read `authorization_endpoint` (`string`).
|
|
302
|
+
- Read `issuer` (`string`).
|
|
303
|
+
- Read `jwks_uri` (`string`).
|
|
304
|
+
- Read `token_endpoint` (`string`).
|
|
305
|
+
- Read `userinfo_endpoint` (`string`).
|
|
306
|
+
|
|
307
|
+
## Built-in operations used by this file
|
|
308
|
+
|
|
309
|
+
- `HttpResponse.body` (`Bytes`): The typed response body.
|
|
310
|
+
- `HttpResponse.headers` (`Headers`): Immutable response headers. Duplicate Set-Cookie values are preserved.
|
|
311
|
+
- `HttpResponse.status` (`int`): HTTP response status.
|
|
312
|
+
- `Bytes.text` (no inputs) → `string`: Decode UTF-8 strictly. Invalid input raises ConversionError; embedded NUL is preserved. Can fail with `ConversionError`.
|
|
313
|
+
- `Headers.get` (`name`: `string`) → `optional string`: Read the first case-insensitive header value, or null.
|
|
314
|
+
- `Json.decode` (no inputs) → `IdClaims`: Decode a checked record or data type: json.decode<Profile>(). Unknown fields, type mismatches, and validation errors are rejected. Can fail with `JsonError`.
|
|
315
|
+
- `List<RsaJwk>.get` (`index`: `int`) → `RsaJwk`: Read a zero-based position. An invalid index raises checked IndexError. Reference results grant reading. Can fail with `IndexError`.
|
|
316
|
+
- `List<RsaJwk>.length` (no inputs) → `int`: Read the number of elements.
|
|
317
|
+
- `assert` (`condition`: `bool`) → `void`: Assert a bool in a test case or its setup. Catching an assertion failure cannot make the case pass; every case must execute an assertion.
|
|
318
|
+
- `string.bytes` (no inputs) → `Bytes`: Encode this string as immutable UTF-8 bytes.
|
|
319
|
+
- `string.length` (no inputs) → `int`: Read the number of UTF-8 bytes. Unicode text is preserved losslessly.
|
|
320
|
+
- `string.startsWith` (`prefix`: `string`) → `bool`: Test an exact prefix.
|
|
321
|
+
|
|
322
|
+
[Full built-in reference](https://greenpandastudios.github.io/augscript/language-constructs).
|
|
323
|
+
|
|
324
|
+
## Shared language rules
|
|
325
|
+
|
|
326
|
+
See the [language reference](https://greenpandastudios.github.io/augscript/reference) for numeric, equality, ownership, and task rules.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import SessionClaims and SessionError from contracts
|
|
2
|
+
import settings and SigningKeys and KeyError from common
|
|
3
|
+
import Crypto and verifyJwt and JwtError from august.crypto
|
|
4
|
+
import Clock from august.time
|
|
5
|
+
import ExpiringStore from august.memory
|
|
6
|
+
|
|
7
|
+
/** An app session has its own key, issuer, audience and token type. A live registry entry is required so logout revokes a signed token immediately. */
|
|
8
|
+
authenticate(optional string token, resolve Crypto crypto, resolve Clock clock, resolve SigningKeys keys, resolve ExpiringStore<SessionClaims> sessions) returns SessionClaims uses crypto.publicRsa and crypto.decodeBase64url and crypto.verifyRsa and crypto.equal and clock.now and keys.session and sessions.get unless SessionError and KeyError and TimeError:
|
|
9
|
+
match token:
|
|
10
|
+
when null:
|
|
11
|
+
throw SessionError()
|
|
12
|
+
when some value:
|
|
13
|
+
try:
|
|
14
|
+
publicKey = crypto.publicRsa(key=keys.session())
|
|
15
|
+
claims = verifyJwt(token=value, publicKey, kid="session-1", tokenType="august-session+jwt").decode<SessionClaims>()
|
|
16
|
+
config = settings()
|
|
17
|
+
now = clock.now()
|
|
18
|
+
if claims.iss != config.baseUrl + "/app" or claims.aud != "august-app" or claims.sub.length() == 0 or claims.exp <= now or claims.iat > now + 30 or claims.iat < now - config.sessionSeconds or claims.exp <= claims.iat or claims.exp > now + config.sessionSeconds + 30:
|
|
19
|
+
throw SessionError()
|
|
20
|
+
if (not claims.jti.isToken(min=43, max=43)) or (not claims.csrf.isToken(min=43, max=43)):
|
|
21
|
+
throw SessionError()
|
|
22
|
+
match sessions.get(key=claims.jti, now=now):
|
|
23
|
+
when null:
|
|
24
|
+
throw SessionError()
|
|
25
|
+
when some saved:
|
|
26
|
+
if saved.sub != claims.sub or saved.exp != claims.exp or (not crypto.equal(left=saved.csrf.bytes(), right=claims.csrf.bytes())):
|
|
27
|
+
throw SessionError()
|
|
28
|
+
return claims
|
|
29
|
+
catch CryptoError error:
|
|
30
|
+
throw SessionError()
|
|
31
|
+
catch JwtError error:
|
|
32
|
+
throw SessionError()
|
|
33
|
+
catch JsonError error:
|
|
34
|
+
throw SessionError()
|