├── .gitattributes ├── ballerina ├── icon.png ├── tests │ ├── resources │ │ ├── keystore │ │ │ ├── ballerinaKeystore.p12 │ │ │ └── ballerinaTruststore.p12 │ │ ├── cert │ │ │ ├── invalidPublic.crt │ │ │ └── public.crt │ │ └── key │ │ │ ├── private.key │ │ │ └── encryptedPrivate.key │ ├── client_self_signed_jwt_auth_provider_test.bal │ ├── listener_jwt_auth_provider_test.bal │ └── test_utils.bal ├── CompilerPlugin.toml ├── Ballerina.toml ├── init.bal ├── jwt_errors.bal ├── client_self_signed_jwt_auth_provider.bal ├── README.md ├── jwt_commons.bal ├── Dependencies.toml ├── listener_jwt_auth_provider.bal ├── build.gradle └── jwt_issuer.bal ├── examples └── order-management-service │ ├── .github │ └── Readme.md │ ├── sts │ ├── Ballerina.toml │ ├── resources │ │ ├── public.crt │ │ └── private.key │ └── sts.bal │ ├── order-management-service.png │ ├── order_service │ ├── Ballerina.toml │ ├── resources │ │ ├── public.crt │ │ └── private.key │ ├── modules │ │ └── representations │ │ │ └── representations.bal │ └── order_service.bal │ └── inventory_service │ ├── Ballerina.toml │ ├── modules │ ├── representations │ │ └── representations.bal │ └── inventory │ │ └── inventory.bal │ ├── resources │ ├── public.crt │ └── private.key │ └── inventory_service.bal ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── load-tests └── order_management_service │ ├── deployment │ ├── kustomization.yaml │ └── ingress.yaml │ ├── src │ ├── Ballerina.toml │ ├── Cloud.toml │ ├── resources │ │ ├── sts │ │ │ ├── public.crt │ │ │ └── private.key │ │ └── order_service │ │ │ ├── public.crt │ │ │ └── private.key │ ├── order_service.bal │ └── sts.bal │ └── scripts │ ├── run.sh │ └── http-post-request.jmx ├── codecov.yml ├── compiler-plugin ├── src │ └── main │ │ ├── resources │ │ └── rules.json │ │ └── java │ │ ├── module-info.java │ │ └── io │ │ └── ballerina │ │ └── stdlib │ │ └── jwt │ │ └── compiler │ │ ├── staticcodeanalyzer │ │ ├── RuleFactory.java │ │ ├── JwtCodeAnalyzer.java │ │ ├── RuleImpl.java │ │ └── JwtRule.java │ │ └── JwtCompilerPlugin.java └── build.gradle ├── .github ├── pull_request_template.md ├── CODEOWNERS └── workflows │ ├── trivy-scan.yml │ ├── publish-release.yml │ ├── build-timestamped-master.yml │ ├── fossa_scan.yml │ ├── pull-request.yml │ ├── process-load-test-result.yml │ ├── central-publish.yml │ ├── trigger-load-tests.yml │ ├── build-with-bal-test-graalvm.yml │ └── update_specs.yml ├── compiler-plugin-tests ├── src │ └── test │ │ ├── resources │ │ ├── static_code_analyzer │ │ │ └── ballerina_packages │ │ │ │ └── rule1 │ │ │ │ ├── Ballerina.toml │ │ │ │ ├── inline_pos_arg.bal │ │ │ │ ├── inline_named_arg.bal │ │ │ │ ├── function_pos_arg_capture_pattern.bal │ │ │ │ ├── function_named_arg_capture_pattern.bal │ │ │ │ ├── module_pos_arg_capture_pattern.bal │ │ │ │ ├── module_named_arg_capture_pattern.bal │ │ │ │ ├── function_pos_arg_list_pattern.bal │ │ │ │ ├── module_pos_arg_list_pattern.bal │ │ │ │ ├── function_named_arg_list_pattern.bal │ │ │ │ └── module_named_arg_list_pattern.bal │ │ └── testng.xml │ │ └── java │ │ └── io │ │ └── ballerina │ │ └── stdlib │ │ └── jwt │ │ └── compiler │ │ └── staticcodeanalyzer │ │ └── StaticCodeAnalyzerTest.java └── build.gradle ├── .gitignore ├── gradle.properties ├── native ├── src │ └── main │ │ └── java │ │ ├── module-info.java │ │ └── io │ │ └── ballerina │ │ └── stdlib │ │ └── jwt │ │ ├── ModuleUtils.java │ │ ├── JwtUtils.java │ │ └── JwtConstants.java └── build.gradle ├── settings.gradle ├── changelog.md ├── gradlew.bat ├── docs └── proposals │ └── enable-crypto-key-usage.md ├── README.md ├── gradlew └── LICENSE /.gitattributes: -------------------------------------------------------------------------------- 1 | # Ensure all Java files use LF. 2 | *.java eol=lf 3 | -------------------------------------------------------------------------------- /ballerina/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ballerina-platform/module-ballerina-jwt/HEAD/ballerina/icon.png -------------------------------------------------------------------------------- /examples/order-management-service/.github/Readme.md: -------------------------------------------------------------------------------- 1 | ../../order-management-service/A Guide on Securing Ballerina Rest APIs with JWT Auth.md -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ballerina-platform/module-ballerina-jwt/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /load-tests/order_management_service/deployment/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - order_management_service.yaml 3 | - ingress.yaml 4 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | precision: 2 3 | round: down 4 | range: "60...80" 5 | status: 6 | project: 7 | default: 8 | target: 80 9 | -------------------------------------------------------------------------------- /examples/order-management-service/sts/Ballerina.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | org = "jwt" 3 | name = "sts" 4 | version = "1.0.0" 5 | 6 | [build-options] 7 | observabilityIncluded = true 8 | -------------------------------------------------------------------------------- /ballerina/tests/resources/keystore/ballerinaKeystore.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ballerina-platform/module-ballerina-jwt/HEAD/ballerina/tests/resources/keystore/ballerinaKeystore.p12 -------------------------------------------------------------------------------- /ballerina/tests/resources/keystore/ballerinaTruststore.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ballerina-platform/module-ballerina-jwt/HEAD/ballerina/tests/resources/keystore/ballerinaTruststore.p12 -------------------------------------------------------------------------------- /examples/order-management-service/order-management-service.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ballerina-platform/module-ballerina-jwt/HEAD/examples/order-management-service/order-management-service.png -------------------------------------------------------------------------------- /examples/order-management-service/order_service/Ballerina.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | org = "jwt" 3 | name = "order_service" 4 | version = "1.0.0" 5 | 6 | [build-options] 7 | observabilityIncluded = true 8 | -------------------------------------------------------------------------------- /examples/order-management-service/inventory_service/Ballerina.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | org = "jwt" 3 | name = "inventory_service" 4 | version = "1.0.0" 5 | 6 | [build-options] 7 | observabilityIncluded = true 8 | -------------------------------------------------------------------------------- /compiler-plugin/src/main/resources/rules.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "kind": "VULNERABILITY", 5 | "description": "Avoid using weak cipher algorithms when signing and verifying JWTs" 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /load-tests/order_management_service/src/Ballerina.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | org = "jwt" 3 | name = "order_management_service" 4 | version = "1.0.0" 5 | 6 | [build-options] 7 | observabilityIncluded = false 8 | cloud = "k8s" 9 | -------------------------------------------------------------------------------- /ballerina/CompilerPlugin.toml: -------------------------------------------------------------------------------- 1 | [plugin] 2 | id = "jwt-compiler-plugin" 3 | class = "io.ballerina.stdlib.jwt.compiler.JwtCompilerPlugin" 4 | 5 | [[dependency]] 6 | path = "../compiler-plugin/build/libs/jwt-compiler-plugin-2.15.1.jar" 7 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Purpose 2 | 3 | ## Examples 4 | 5 | ## Checklist 6 | - [ ] Linked to an issue 7 | - [ ] Updated the changelog 8 | - [ ] Added tests 9 | - [ ] Updated the spec 10 | - [ ] Checked native-image compatibility 11 | -------------------------------------------------------------------------------- /compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/Ballerina.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | org = "wso2" 3 | name = "rule1" 4 | version = "0.1.0" 5 | distribution = "2201.12.3" 6 | 7 | [build-options] 8 | observabilityIncluded = true 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Lines starting with '#' are comments. 2 | # Each line is a file pattern followed by one or more owners. 3 | 4 | # See: https://help.github.com/articles/about-codeowners/ 5 | 6 | # These owners will be the default owners for everything in the repo. 7 | * @NipunaRanasinghe @ayeshLK @Nuvindu @shafreenAnfar 8 | -------------------------------------------------------------------------------- /load-tests/order_management_service/src/Cloud.toml: -------------------------------------------------------------------------------- 1 | [container.image] 2 | repository="ballerina" 3 | name="order_management_service" 4 | 5 | [cloud.deployment] 6 | min_memory="256Mi" 7 | max_memory="512Mi" 8 | min_cpu="200m" 9 | max_cpu="1000m" 10 | 11 | [cloud.deployment.autoscaling] 12 | min_replicas=1 13 | max_replicas=1 14 | -------------------------------------------------------------------------------- /.github/workflows/trivy-scan.yml: -------------------------------------------------------------------------------- 1 | name: Trivy 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "30 20 * * *" 7 | 8 | jobs: 9 | call_workflow: 10 | name: Run Trivy Scan Workflow 11 | if: ${{ github.repository_owner == 'ballerina-platform' }} 12 | uses: ballerina-platform/ballerina-library/.github/workflows/trivy-scan-template.yml@main 13 | secrets: inherit 14 | -------------------------------------------------------------------------------- /.github/workflows/publish-release.yml: -------------------------------------------------------------------------------- 1 | name: Publish Release 2 | 3 | on: 4 | workflow_dispatch: 5 | repository_dispatch: 6 | types: [stdlib-release-pipeline] 7 | 8 | jobs: 9 | call_workflow: 10 | name: Run Release Workflow 11 | if: ${{ github.repository_owner == 'ballerina-platform' }} 12 | uses: ballerina-platform/ballerina-library/.github/workflows/release-package-template.yml@main 13 | secrets: inherit 14 | with: 15 | package-name: jwt 16 | package-org: ballerina 17 | -------------------------------------------------------------------------------- /.github/workflows/build-timestamped-master.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths-ignore: 8 | - "*.md" 9 | - "docs/**" 10 | - "load-tests/**" 11 | workflow_dispatch: 12 | 13 | jobs: 14 | call_workflow: 15 | name: Run Build Workflow 16 | if: ${{ github.repository_owner == 'ballerina-platform' }} 17 | uses: ballerina-platform/ballerina-library/.github/workflows/build-timestamp-master-template.yml@main 18 | secrets: inherit 19 | -------------------------------------------------------------------------------- /.github/workflows/fossa_scan.yml: -------------------------------------------------------------------------------- 1 | name: Fossa Scan 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | - cron: '30 18 * * *' # 00:00 in LK time (GMT+5:30) 6 | jobs: 7 | fossa-scan: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: fossas/fossa-action@main 12 | env: 13 | packageUser: ${{ secrets.BALLERINA_BOT_USERNAME }} 14 | packagePAT: ${{ secrets.BALLERINA_BOT_TOKEN }} 15 | with: 16 | api-key: ${{secrets.FOSSA_APIKEY}} 17 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yml: -------------------------------------------------------------------------------- 1 | name: PR Build 2 | 3 | concurrency: 4 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} 5 | cancel-in-progress: true 6 | 7 | on: [pull_request] 8 | 9 | jobs: 10 | call_workflow: 11 | name: Run PR Build Workflow 12 | if: ${{ github.repository_owner == 'ballerina-platform' }} 13 | uses: ballerina-platform/ballerina-library/.github/workflows/pull-request-build-template.yml@main 14 | with: 15 | additional-windows-test-flags: "-Pdisable=jwks" 16 | secrets: inherit 17 | -------------------------------------------------------------------------------- /ballerina/Ballerina.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | org = "ballerina" 3 | name = "jwt" 4 | version = "2.15.1" 5 | authors = ["Ballerina"] 6 | keywords = ["security", "authentication", "jwt", "jwk", "jws"] 7 | repository = "https://github.com/ballerina-platform/module-ballerina-jwt" 8 | icon = "icon.png" 9 | license = ["Apache-2.0"] 10 | distribution = "2201.12.0" 11 | 12 | [platform.java21] 13 | graalvmCompatible = true 14 | 15 | [[platform.java21.dependency]] 16 | groupId = "io.ballerina.stdlib" 17 | artifactId = "jwt-native" 18 | version = "2.15.1" 19 | path = "../native/build/libs/jwt-native-2.15.1.jar" 20 | -------------------------------------------------------------------------------- /.github/workflows/process-load-test-result.yml: -------------------------------------------------------------------------------- 1 | name: Process load test results 2 | 3 | on: 4 | repository_dispatch: 5 | types: [jwt-load-test] 6 | 7 | jobs: 8 | call_stdlib_process_load_test_results_workflow: 9 | name: Run StdLib Process Load Test Results Workflow 10 | uses: ballerina-platform/ballerina-library/.github/workflows/process-load-test-results-template.yml@main 11 | with: 12 | results: ${{ toJson(github.event.client_payload.results) }} 13 | secrets: 14 | ballerina_bot_token: ${{ secrets.BALLERINA_BOT_TOKEN }} 15 | ballerina_reviewer_bot_token: ${{ secrets.BALLERINA_REVIEWER_BOT_TOKEN }} 16 | -------------------------------------------------------------------------------- /.github/workflows/central-publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to the Ballerina central 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | environment: 7 | type: choice 8 | description: Select Environment 9 | required: true 10 | options: 11 | - DEV CENTRAL 12 | - STAGE CENTRAL 13 | 14 | jobs: 15 | call_workflow: 16 | name: Run Central Publish Workflow 17 | if: ${{ github.repository_owner == 'ballerina-platform' }} 18 | uses: ballerina-platform/ballerina-library/.github/workflows/central-publish-template.yml@main 19 | secrets: inherit 20 | with: 21 | environment: ${{ github.event.inputs.environment }} 22 | -------------------------------------------------------------------------------- /load-tests/order_management_service/deployment/ingress.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: networking.k8s.io/v1 3 | kind: Ingress 4 | metadata: 5 | name: order-management-service 6 | annotations: 7 | kubernetes.io/ingress.class: nginx 8 | nginx.ingress.kubernetes.io/ssl-passthrough: "true" 9 | nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" 10 | spec: 11 | rules: 12 | - host: bal.perf.test 13 | http: 14 | paths: 15 | - path: "/" 16 | pathType: Prefix 17 | backend: 18 | service: 19 | name: order-managemen 20 | port: 21 | number: 9090 22 | tls: 23 | - hosts: 24 | - "bal.perf.test" 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | build 26 | .gradle/ 27 | target 28 | # IDEA Files 29 | .idea/ 30 | *.iml 31 | *.ipr 32 | *.iws 33 | 34 | # MacOS 35 | *.DS_Store 36 | 37 | # Ballerina 38 | velocity.log* 39 | *Ballerina.lock 40 | 41 | # Auto generated Dependencies.toml files 42 | examples/**/Dependencies.toml 43 | load-tests/**/Dependencies.toml 44 | 45 | compiler-plugin-tests/**/target 46 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.caching=true 2 | group=io.ballerina.stdlib 3 | version=2.15.2-SNAPSHOT 4 | ballerinaLangVersion=2201.12.0 5 | testngVersion=7.6.1 6 | 7 | checkstylePluginVersion=10.12.0 8 | ballerinaGradlePluginVersion=2.3.0 9 | shadowJarPluginVersion=8.1.1 10 | downloadPluginVersion=5.4.0 11 | releasePluginVersion=2.8.0 12 | spotbugsPluginVersion=6.0.18 13 | gsonVersion=2.7 14 | balScanVersion=0.10.0 15 | jacksonDatabindVersion=2.17.2 16 | 17 | stdlibCacheVersion=3.10.0 18 | stdlibCryptoVersion=2.9.0 19 | stdlibLogVersion=2.12.0 20 | stdlibTimeVersion=2.7.0 21 | # Transitive dependencies 22 | stdlibTaskVersion=2.7.0 23 | stdlibConstraintVersion=1.7.0 24 | stdlibIoVersion=1.8.0 25 | observeVersion=1.5.0 26 | observeInternalVersion=1.5.0 27 | -------------------------------------------------------------------------------- /native/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 WSO2 LLC. (http://www.wso2.com). 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 14 | * OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | module io.ballerina.stdlib.jwt { 20 | requires io.ballerina.runtime; 21 | requires java.net.http; 22 | requires io.ballerina.stdlib.crypto; 23 | exports io.ballerina.stdlib.jwt; 24 | } 25 | -------------------------------------------------------------------------------- /ballerina/init.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/jballerina.java; 18 | 19 | isolated function init() { 20 | setModule(); 21 | } 22 | 23 | isolated function setModule() = @java:Method { 24 | 'class: "io.ballerina.stdlib.jwt.ModuleUtils" 25 | } external; 26 | -------------------------------------------------------------------------------- /compiler-plugin/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 WSO2 LLC. (http://www.wso2.com). 3 | * 4 | * WSO2 LLC. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 14 | * OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | module io.ballerina.stdlib.jwt.compiler { 20 | requires io.ballerina.lang; 21 | requires io.ballerina.tools.api; 22 | requires io.ballerina.parser; 23 | requires io.ballerina.scan; 24 | } 25 | -------------------------------------------------------------------------------- /examples/order-management-service/inventory_service/modules/representations/representations.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/http; 18 | 19 | public type OrderItem record {| 20 | string category; 21 | string code; 22 | int qty; 23 | |}; 24 | 25 | public type InventoryUpdated record {| 26 | *http:Ok; 27 | |}; 28 | -------------------------------------------------------------------------------- /compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/inline_pos_arg.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 WSO2 LLC. (http://www.wso2.com). 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 13 | // OF ANY KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/jwt; 18 | 19 | public function inlinePosArg() returns error? { 20 | string _ = check jwt:issue({ 21 | issuer: "ballerina", 22 | expTime: 3600, 23 | signatureConfig: { 24 | algorithm: jwt:NONE 25 | } 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/inline_named_arg.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 WSO2 LLC. (http://www.wso2.com). 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 13 | // OF ANY KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/jwt; 18 | 19 | public function inlineNamedArg() returns error? { 20 | string _ = check jwt:issue(issuerConfig = { 21 | issuer: "ballerina", 22 | expTime: 3600, 23 | signatureConfig: { 24 | algorithm: jwt:NONE 25 | } 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /load-tests/order_management_service/scripts/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | # Copyright 2021 WSO2 Inc. (http://wso2.org) 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # ---------------------------------------------------------------------------- 17 | # Execution script for ballerina performance tests 18 | # ---------------------------------------------------------------------------- 19 | set -e 20 | source base-scenario.sh 21 | 22 | jmeter -n -t "$scriptsDir/"http-post-request.jmx -l "$resultsDir/"original.jtl -Jusers="$concurrent_users" -Jduration=3600 -Jhost=bal.perf.test -Jport=443 -Jprotocol=https -Jpath=order $payload_flags 23 | -------------------------------------------------------------------------------- /compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/function_pos_arg_capture_pattern.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 WSO2 LLC. (http://www.wso2.com). 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 13 | // OF ANY KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/jwt; 18 | 19 | public function functionPosArgCapturePattern() returns error? { 20 | jwt:IssuerConfig issuerConfig = { 21 | issuer: "ballerina", 22 | expTime: 3600, 23 | signatureConfig: { 24 | algorithm: jwt:NONE 25 | } 26 | }; 27 | 28 | string _ = check jwt:issue(issuerConfig); 29 | } 30 | -------------------------------------------------------------------------------- /compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/function_named_arg_capture_pattern.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 WSO2 LLC. (http://www.wso2.com). 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 13 | // OF ANY KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/jwt; 18 | 19 | public function functionNamedArgCapturePattern() returns error? { 20 | jwt:IssuerConfig issuerConfig = { 21 | issuer: "ballerina", 22 | expTime: 3600, 23 | signatureConfig: { 24 | algorithm: jwt:NONE 25 | } 26 | }; 27 | 28 | string _ = check jwt:issue(issuerConfig = issuerConfig); 29 | } 30 | -------------------------------------------------------------------------------- /compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/module_pos_arg_capture_pattern.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 WSO2 LLC. (http://www.wso2.com). 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 13 | // OF ANY KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/jwt; 18 | 19 | jwt:IssuerConfig issuerConfigModulePosArgCapturePattern = { 20 | issuer: "ballerina", 21 | expTime: 3600, 22 | signatureConfig: { 23 | algorithm: jwt:NONE 24 | } 25 | }; 26 | 27 | public function modulePosArgCapturePattern() returns error? { 28 | string _ = check jwt:issue(issuerConfigModulePosArgCapturePattern); 29 | } 30 | -------------------------------------------------------------------------------- /compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/module_named_arg_capture_pattern.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 WSO2 LLC. (http://www.wso2.com). 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 13 | // OF ANY KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/jwt; 18 | 19 | jwt:IssuerConfig issuerConfigModuleNamedArgCapturePattern = { 20 | issuer: "ballerina", 21 | expTime: 3600, 22 | signatureConfig: { 23 | algorithm: jwt:NONE 24 | } 25 | }; 26 | 27 | public function moduleNamedArgCapturePattern() returns error? { 28 | string _ = check jwt:issue(issuerConfig = issuerConfigModuleNamedArgCapturePattern); 29 | } 30 | -------------------------------------------------------------------------------- /compiler-plugin-tests/src/test/resources/testng.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/function_pos_arg_list_pattern.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 WSO2 LLC. (http://www.wso2.com). 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 13 | // OF ANY KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/jwt; 18 | 19 | public function functionPosArgListPattern() returns error? { 20 | [jwt:IssuerConfig] [issuerConfig] = [ 21 | { 22 | issuer: "ballerina", 23 | expTime: 3600, 24 | signatureConfig: { 25 | algorithm: jwt:NONE 26 | } 27 | } 28 | ]; 29 | 30 | string _ = check jwt:issue(issuerConfig); 31 | } 32 | -------------------------------------------------------------------------------- /compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/module_pos_arg_list_pattern.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 WSO2 LLC. (http://www.wso2.com). 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 13 | // OF ANY KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/jwt; 18 | 19 | [jwt:IssuerConfig] [issuerConfigModulePosArgListPattern] = [ 20 | { 21 | issuer: "ballerina", 22 | expTime: 3600, 23 | signatureConfig: { 24 | algorithm: jwt:NONE 25 | } 26 | } 27 | ]; 28 | 29 | public function modulePosArgListPattern() returns error? { 30 | string _ = check jwt:issue(issuerConfigModulePosArgListPattern); 31 | } 32 | -------------------------------------------------------------------------------- /compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/function_named_arg_list_pattern.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 WSO2 LLC. (http://www.wso2.com). 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 13 | // OF ANY KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/jwt; 18 | 19 | public function functionNamedArgListPattern() returns error? { 20 | [jwt:IssuerConfig] [issuerConfig] = [ 21 | { 22 | issuer: "ballerina", 23 | expTime: 3600, 24 | signatureConfig: { 25 | algorithm: jwt:NONE 26 | } 27 | } 28 | ]; 29 | 30 | string _ = check jwt:issue(issuerConfig = issuerConfig); 31 | } 32 | -------------------------------------------------------------------------------- /compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/module_named_arg_list_pattern.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 WSO2 LLC. (http://www.wso2.com). 2 | // 3 | // WSO2 LLC. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 13 | // OF ANY KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/jwt; 18 | 19 | [jwt:IssuerConfig] [issuerConfigModuleNamedArgListPattern] = [ 20 | { 21 | issuer: "ballerina", 22 | expTime: 3600, 23 | signatureConfig: { 24 | algorithm: jwt:NONE 25 | } 26 | } 27 | ]; 28 | 29 | public function moduleNamedArgListPattern() returns error? { 30 | string _ = check jwt:issue(issuerConfig = issuerConfigModuleNamedArgListPattern); 31 | } 32 | -------------------------------------------------------------------------------- /ballerina/tests/resources/cert/invalidPublic.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDJjCCAg6gAwIBAgIIQu5yrnbBC2gwDQYJKoZIhvcNAQEFBQAwNjE0MDIGA1UE 3 | AxMrZmVkZXJhdGVkLXNpZ25vbi5zeXN0ZW0uZ3NlcnZpY2VhY2NvdW50LmNvbTAe 4 | Fw0yMTAyMDQwNDI5NDhaFw0yMTAyMjAxNjQ0NDhaMDYxNDAyBgNVBAMTK2ZlZGVy 5 | YXRlZC1zaWdub24uc3lzdGVtLmdzZXJ2aWNlYWNjb3VudC5jb20wggEiMA0GCSqG 6 | SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDeDjrDi5FgHHwJcVq5aHpznI7hrH9UO7nL 7 | yaX8l3gbHvXXXnk9N20t5G7Bk6EnqdHLxi8sU9dz9pe0eJ+dCyL1i4j++jssYba/ 8 | cILGHqd1kGMh4WTESswODgQu3YNDQ+AlRBQAoKSXb+CRVX2+HejFqG/8GkiC/WS5 9 | iXJBCLklZ4XvMAibQVpn4slPbs1uwK6BxkgNCD2zO1M3Xwvf4/+RjC8CQpjZsZg1 10 | TX2osUzWBmTNxwUQNm/cvMDPupt1aTAKgrTRNRQfxM2OqYrrtvK1jOASCR8X2r2z 11 | MRDlOtEvS4WD0vFPeRJMtipg5VpQhfe5oKHBpr5tC8e42avK7uL3AgMBAAGjODA2 12 | MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsG 13 | AQUFBwMCMA0GCSqGSIb3DQEBBQUAA4IBAQBMFTVY0njCVNoiZYcmchwkXGiAh9s3 14 | 0jNhyX/9TMDIcMUq2ozVM1kaXCWDMqcX7ifF4h7TL/gS3xi9l36AlC5C+TwAl0dX 15 | 83s6DpfHNaub46cgHed7OPjbX/pn2vwL5EQ9asRhZhjA1qcU+Kj/6MXl1AKS6bCq 16 | TkkAH65evkxIaNHUaJ/p931hbzhhOfprrIidAg56E/DTlV9Evq2P8aYX6JDzy+Qi 17 | Xu/Stepb/YTGEYDub687EwthMWU07iygj1sjSD39ms+HlF7/zvnafPKv4bNjuYk1 18 | 215clsOH3rOvNd+npd2cl3l8GnDEIGqBoY98SABAOc5Zk25Hl/PpKt18 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /ballerina/jwt_errors.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/log; 18 | 19 | # Represents the error type of the module. This will be returned if an error occurred while issuing/validating a JWT 20 | # or any operation related to JWT auth providers. 21 | public type Error distinct error; 22 | 23 | // Logs and prepares the `error` as a `jwt:Error`. 24 | isolated function prepareError(string message, error? err = ()) returns Error { 25 | log:printDebug(message, 'error = err); 26 | if err is error { 27 | return error Error(message, err); 28 | } 29 | return error Error(message); 30 | } 31 | -------------------------------------------------------------------------------- /compiler-plugin/src/main/java/io/ballerina/stdlib/jwt/compiler/staticcodeanalyzer/RuleFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 WSO2 LLC. (http://www.wso2.com). 3 | * 4 | * WSO2 LLC. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 14 | * OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | package io.ballerina.stdlib.jwt.compiler.staticcodeanalyzer; 20 | 21 | import io.ballerina.scan.Rule; 22 | import io.ballerina.scan.RuleKind; 23 | 24 | /** 25 | * {@code RuleFactory} contains the logic to create a {@link Rule}. 26 | */ 27 | public final class RuleFactory { 28 | 29 | private RuleFactory() {} 30 | 31 | public static Rule createRule(int id, String description, RuleKind kind) { 32 | return new RuleImpl(id, description, kind); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ballerina/tests/resources/cert/public.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDdzCCAl+gAwIBAgIEfP3e8zANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJV 3 | UzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxDTALBgNVBAoT 4 | BFdTTzIxDTALBgNVBAsTBFdTTzIxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xNzEw 5 | MjQwNTQ3NThaFw0zNzEwMTkwNTQ3NThaMGQxCzAJBgNVBAYTAlVTMQswCQYDVQQI 6 | EwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzENMAsGA1UEChMEV1NPMjENMAsG 7 | A1UECxMEV1NPMjESMBAGA1UEAxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF 8 | AAOCAQ8AMIIBCgKCAQEAgVyi6fViVLiZKEnw59xzNi1lcYh6z9dZnug+F9gKqFIg 9 | mdcPe+qtS7gZc1jYTjWMCbx13sFLkZqNHeDUadpmtKo3TDduOl1sqM6cz3yXb6L3 10 | 4k/leh50mzIPNmaaXxd3vOQoK4OpkgO1n32mh6+tkp3sbHmfYqDQrkVK1tmYNtPJ 11 | ffSCLT+CuIhnJUJco7N0unax+ySZN67/AX++sJpqAhAIZJzrRi6ueN3RFCIxYDXS 12 | MvxrEmOdn4gOC0o1Ar9u5Bp9N52sqqGbN1x6jNKi3bfUj122Hu5e+Y9KOmfbchhQ 13 | il2P81cIi30VKgyDn5DeWEuDoYredk4+6qAZrxMw+wIDAQABozEwLzAOBgNVHQ8B 14 | Af8EBAMCBaAwHQYDVR0OBBYEFNmtrQ36j6tUGhKrfW9qWWE7KFzMMA0GCSqGSIb3 15 | DQEBCwUAA4IBAQAv3yOwgbtOu76eJMl1BCcgTFgaMUBZoUjK9Un6HGjKEgYz/YWS 16 | ZFlY/qH5rT01DWQevUZB626d5ZNdzSBZRlpsxbf9IE/ursNHwHx9ua6fB7yHUCzC 17 | 1ZMp1lvBHABi7wcA+5nbV6zQ7HDmBXFhJfbgH1iVmA1KcvDeBPSJ/scRGasZ5q2W 18 | 3IenDNrfPIUhD74tFiCiqNJO91qD/LO+++3XeZzfPh8NRKkiPX7dB8WJ3YNBuQAv 19 | gRWTISpSSXLmqMb+7MPQVgecsepZdk8CwkRLxh3RKPJMjigmCgyvkSaoDMKAYC3i 20 | YjfUTiJ57UeqoSl0IaOFJ0wfZRFh+UytlDZa 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /native/src/main/java/io/ballerina/stdlib/jwt/ModuleUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | package io.ballerina.stdlib.jwt; 20 | 21 | import io.ballerina.runtime.api.Environment; 22 | import io.ballerina.runtime.api.Module; 23 | 24 | /** 25 | * Utility functions relevant to module operations. 26 | * 27 | * @since 2.0.0 28 | */ 29 | public class ModuleUtils { 30 | 31 | private static Module jwtModule; 32 | 33 | private ModuleUtils() {} 34 | 35 | public static void setModule(Environment env) { 36 | jwtModule = env.getCurrentModule(); 37 | } 38 | 39 | public static Module getModule() { 40 | return jwtModule; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/order-management-service/sts/resources/public.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDdzCCAl+gAwIBAgIEfP3e8zANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJV 3 | UzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxDTALBgNVBAoT 4 | BFdTTzIxDTALBgNVBAsTBFdTTzIxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xNzEw 5 | MjQwNTQ3NThaFw0zNzEwMTkwNTQ3NThaMGQxCzAJBgNVBAYTAlVTMQswCQYDVQQI 6 | EwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzENMAsGA1UEChMEV1NPMjENMAsG 7 | A1UECxMEV1NPMjESMBAGA1UEAxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF 8 | AAOCAQ8AMIIBCgKCAQEAgVyi6fViVLiZKEnw59xzNi1lcYh6z9dZnug+F9gKqFIg 9 | mdcPe+qtS7gZc1jYTjWMCbx13sFLkZqNHeDUadpmtKo3TDduOl1sqM6cz3yXb6L3 10 | 4k/leh50mzIPNmaaXxd3vOQoK4OpkgO1n32mh6+tkp3sbHmfYqDQrkVK1tmYNtPJ 11 | ffSCLT+CuIhnJUJco7N0unax+ySZN67/AX++sJpqAhAIZJzrRi6ueN3RFCIxYDXS 12 | MvxrEmOdn4gOC0o1Ar9u5Bp9N52sqqGbN1x6jNKi3bfUj122Hu5e+Y9KOmfbchhQ 13 | il2P81cIi30VKgyDn5DeWEuDoYredk4+6qAZrxMw+wIDAQABozEwLzAOBgNVHQ8B 14 | Af8EBAMCBaAwHQYDVR0OBBYEFNmtrQ36j6tUGhKrfW9qWWE7KFzMMA0GCSqGSIb3 15 | DQEBCwUAA4IBAQAv3yOwgbtOu76eJMl1BCcgTFgaMUBZoUjK9Un6HGjKEgYz/YWS 16 | ZFlY/qH5rT01DWQevUZB626d5ZNdzSBZRlpsxbf9IE/ursNHwHx9ua6fB7yHUCzC 17 | 1ZMp1lvBHABi7wcA+5nbV6zQ7HDmBXFhJfbgH1iVmA1KcvDeBPSJ/scRGasZ5q2W 18 | 3IenDNrfPIUhD74tFiCiqNJO91qD/LO+++3XeZzfPh8NRKkiPX7dB8WJ3YNBuQAv 19 | gRWTISpSSXLmqMb+7MPQVgecsepZdk8CwkRLxh3RKPJMjigmCgyvkSaoDMKAYC3i 20 | YjfUTiJ57UeqoSl0IaOFJ0wfZRFh+UytlDZa 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /load-tests/order_management_service/src/resources/sts/public.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDdzCCAl+gAwIBAgIEfP3e8zANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJV 3 | UzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxDTALBgNVBAoT 4 | BFdTTzIxDTALBgNVBAsTBFdTTzIxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xNzEw 5 | MjQwNTQ3NThaFw0zNzEwMTkwNTQ3NThaMGQxCzAJBgNVBAYTAlVTMQswCQYDVQQI 6 | EwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzENMAsGA1UEChMEV1NPMjENMAsG 7 | A1UECxMEV1NPMjESMBAGA1UEAxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF 8 | AAOCAQ8AMIIBCgKCAQEAgVyi6fViVLiZKEnw59xzNi1lcYh6z9dZnug+F9gKqFIg 9 | mdcPe+qtS7gZc1jYTjWMCbx13sFLkZqNHeDUadpmtKo3TDduOl1sqM6cz3yXb6L3 10 | 4k/leh50mzIPNmaaXxd3vOQoK4OpkgO1n32mh6+tkp3sbHmfYqDQrkVK1tmYNtPJ 11 | ffSCLT+CuIhnJUJco7N0unax+ySZN67/AX++sJpqAhAIZJzrRi6ueN3RFCIxYDXS 12 | MvxrEmOdn4gOC0o1Ar9u5Bp9N52sqqGbN1x6jNKi3bfUj122Hu5e+Y9KOmfbchhQ 13 | il2P81cIi30VKgyDn5DeWEuDoYredk4+6qAZrxMw+wIDAQABozEwLzAOBgNVHQ8B 14 | Af8EBAMCBaAwHQYDVR0OBBYEFNmtrQ36j6tUGhKrfW9qWWE7KFzMMA0GCSqGSIb3 15 | DQEBCwUAA4IBAQAv3yOwgbtOu76eJMl1BCcgTFgaMUBZoUjK9Un6HGjKEgYz/YWS 16 | ZFlY/qH5rT01DWQevUZB626d5ZNdzSBZRlpsxbf9IE/ursNHwHx9ua6fB7yHUCzC 17 | 1ZMp1lvBHABi7wcA+5nbV6zQ7HDmBXFhJfbgH1iVmA1KcvDeBPSJ/scRGasZ5q2W 18 | 3IenDNrfPIUhD74tFiCiqNJO91qD/LO+++3XeZzfPh8NRKkiPX7dB8WJ3YNBuQAv 19 | gRWTISpSSXLmqMb+7MPQVgecsepZdk8CwkRLxh3RKPJMjigmCgyvkSaoDMKAYC3i 20 | YjfUTiJ57UeqoSl0IaOFJ0wfZRFh+UytlDZa 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /examples/order-management-service/order_service/resources/public.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDdzCCAl+gAwIBAgIEfP3e8zANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJV 3 | UzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxDTALBgNVBAoT 4 | BFdTTzIxDTALBgNVBAsTBFdTTzIxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xNzEw 5 | MjQwNTQ3NThaFw0zNzEwMTkwNTQ3NThaMGQxCzAJBgNVBAYTAlVTMQswCQYDVQQI 6 | EwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzENMAsGA1UEChMEV1NPMjENMAsG 7 | A1UECxMEV1NPMjESMBAGA1UEAxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF 8 | AAOCAQ8AMIIBCgKCAQEAgVyi6fViVLiZKEnw59xzNi1lcYh6z9dZnug+F9gKqFIg 9 | mdcPe+qtS7gZc1jYTjWMCbx13sFLkZqNHeDUadpmtKo3TDduOl1sqM6cz3yXb6L3 10 | 4k/leh50mzIPNmaaXxd3vOQoK4OpkgO1n32mh6+tkp3sbHmfYqDQrkVK1tmYNtPJ 11 | ffSCLT+CuIhnJUJco7N0unax+ySZN67/AX++sJpqAhAIZJzrRi6ueN3RFCIxYDXS 12 | MvxrEmOdn4gOC0o1Ar9u5Bp9N52sqqGbN1x6jNKi3bfUj122Hu5e+Y9KOmfbchhQ 13 | il2P81cIi30VKgyDn5DeWEuDoYredk4+6qAZrxMw+wIDAQABozEwLzAOBgNVHQ8B 14 | Af8EBAMCBaAwHQYDVR0OBBYEFNmtrQ36j6tUGhKrfW9qWWE7KFzMMA0GCSqGSIb3 15 | DQEBCwUAA4IBAQAv3yOwgbtOu76eJMl1BCcgTFgaMUBZoUjK9Un6HGjKEgYz/YWS 16 | ZFlY/qH5rT01DWQevUZB626d5ZNdzSBZRlpsxbf9IE/ursNHwHx9ua6fB7yHUCzC 17 | 1ZMp1lvBHABi7wcA+5nbV6zQ7HDmBXFhJfbgH1iVmA1KcvDeBPSJ/scRGasZ5q2W 18 | 3IenDNrfPIUhD74tFiCiqNJO91qD/LO+++3XeZzfPh8NRKkiPX7dB8WJ3YNBuQAv 19 | gRWTISpSSXLmqMb+7MPQVgecsepZdk8CwkRLxh3RKPJMjigmCgyvkSaoDMKAYC3i 20 | YjfUTiJ57UeqoSl0IaOFJ0wfZRFh+UytlDZa 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /.github/workflows/trigger-load-tests.yml: -------------------------------------------------------------------------------- 1 | name: Trigger Load Tests 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | tests: 7 | description: > 8 | List of test names. This needs to be filled only if you want to run a specific set of tests. Example: foo,bar 9 | required: false 10 | clusterName: 11 | description: 'Cluster name' 12 | default: 'jwt-perf-cluster-test' 13 | required: false 14 | branch: 15 | description: 'Branch of the given repository' 16 | default: '' 17 | required: false 18 | schedule: 19 | - cron: '0 16 * * *' 20 | 21 | jobs: 22 | call_stdlib_trigger_load_test_workflow: 23 | name: Run StdLib Load Test Workflow 24 | if: ${{ github.event_name != 'schedule' || (github.event_name == 'schedule' && github.repository_owner == 'ballerina-platform') }} 25 | uses: ballerina-platform/ballerina-library/.github/workflows/trigger-load-tests-template.yml@main 26 | with: 27 | repo_name: 'module-ballerina-jwt' 28 | runtime_artifacts_url: 'https://api.github.com/repos/ballerina-platform/module-ballerina-jwt/actions/artifacts' 29 | dispatch_type: 'jwt-load-test' 30 | cluster_name: ${{ inputs.clusterName }} 31 | tests: ${{ inputs.tests }} 32 | branch: ${{ inputs.branch }} 33 | secrets: 34 | ballerina_bot_token: ${{ secrets.BALLERINA_BOT_TOKEN }} 35 | -------------------------------------------------------------------------------- /examples/order-management-service/inventory_service/resources/public.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDdzCCAl+gAwIBAgIEfP3e8zANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJV 3 | UzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxDTALBgNVBAoT 4 | BFdTTzIxDTALBgNVBAsTBFdTTzIxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xNzEw 5 | MjQwNTQ3NThaFw0zNzEwMTkwNTQ3NThaMGQxCzAJBgNVBAYTAlVTMQswCQYDVQQI 6 | EwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzENMAsGA1UEChMEV1NPMjENMAsG 7 | A1UECxMEV1NPMjESMBAGA1UEAxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF 8 | AAOCAQ8AMIIBCgKCAQEAgVyi6fViVLiZKEnw59xzNi1lcYh6z9dZnug+F9gKqFIg 9 | mdcPe+qtS7gZc1jYTjWMCbx13sFLkZqNHeDUadpmtKo3TDduOl1sqM6cz3yXb6L3 10 | 4k/leh50mzIPNmaaXxd3vOQoK4OpkgO1n32mh6+tkp3sbHmfYqDQrkVK1tmYNtPJ 11 | ffSCLT+CuIhnJUJco7N0unax+ySZN67/AX++sJpqAhAIZJzrRi6ueN3RFCIxYDXS 12 | MvxrEmOdn4gOC0o1Ar9u5Bp9N52sqqGbN1x6jNKi3bfUj122Hu5e+Y9KOmfbchhQ 13 | il2P81cIi30VKgyDn5DeWEuDoYredk4+6qAZrxMw+wIDAQABozEwLzAOBgNVHQ8B 14 | Af8EBAMCBaAwHQYDVR0OBBYEFNmtrQ36j6tUGhKrfW9qWWE7KFzMMA0GCSqGSIb3 15 | DQEBCwUAA4IBAQAv3yOwgbtOu76eJMl1BCcgTFgaMUBZoUjK9Un6HGjKEgYz/YWS 16 | ZFlY/qH5rT01DWQevUZB626d5ZNdzSBZRlpsxbf9IE/ursNHwHx9ua6fB7yHUCzC 17 | 1ZMp1lvBHABi7wcA+5nbV6zQ7HDmBXFhJfbgH1iVmA1KcvDeBPSJ/scRGasZ5q2W 18 | 3IenDNrfPIUhD74tFiCiqNJO91qD/LO+++3XeZzfPh8NRKkiPX7dB8WJ3YNBuQAv 19 | gRWTISpSSXLmqMb+7MPQVgecsepZdk8CwkRLxh3RKPJMjigmCgyvkSaoDMKAYC3i 20 | YjfUTiJ57UeqoSl0IaOFJ0wfZRFh+UytlDZa 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /load-tests/order_management_service/src/resources/order_service/public.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDdzCCAl+gAwIBAgIEfP3e8zANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJV 3 | UzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxDTALBgNVBAoT 4 | BFdTTzIxDTALBgNVBAsTBFdTTzIxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xNzEw 5 | MjQwNTQ3NThaFw0zNzEwMTkwNTQ3NThaMGQxCzAJBgNVBAYTAlVTMQswCQYDVQQI 6 | EwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzENMAsGA1UEChMEV1NPMjENMAsG 7 | A1UECxMEV1NPMjESMBAGA1UEAxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF 8 | AAOCAQ8AMIIBCgKCAQEAgVyi6fViVLiZKEnw59xzNi1lcYh6z9dZnug+F9gKqFIg 9 | mdcPe+qtS7gZc1jYTjWMCbx13sFLkZqNHeDUadpmtKo3TDduOl1sqM6cz3yXb6L3 10 | 4k/leh50mzIPNmaaXxd3vOQoK4OpkgO1n32mh6+tkp3sbHmfYqDQrkVK1tmYNtPJ 11 | ffSCLT+CuIhnJUJco7N0unax+ySZN67/AX++sJpqAhAIZJzrRi6ueN3RFCIxYDXS 12 | MvxrEmOdn4gOC0o1Ar9u5Bp9N52sqqGbN1x6jNKi3bfUj122Hu5e+Y9KOmfbchhQ 13 | il2P81cIi30VKgyDn5DeWEuDoYredk4+6qAZrxMw+wIDAQABozEwLzAOBgNVHQ8B 14 | Af8EBAMCBaAwHQYDVR0OBBYEFNmtrQ36j6tUGhKrfW9qWWE7KFzMMA0GCSqGSIb3 15 | DQEBCwUAA4IBAQAv3yOwgbtOu76eJMl1BCcgTFgaMUBZoUjK9Un6HGjKEgYz/YWS 16 | ZFlY/qH5rT01DWQevUZB626d5ZNdzSBZRlpsxbf9IE/ursNHwHx9ua6fB7yHUCzC 17 | 1ZMp1lvBHABi7wcA+5nbV6zQ7HDmBXFhJfbgH1iVmA1KcvDeBPSJ/scRGasZ5q2W 18 | 3IenDNrfPIUhD74tFiCiqNJO91qD/LO+++3XeZzfPh8NRKkiPX7dB8WJ3YNBuQAv 19 | gRWTISpSSXLmqMb+7MPQVgecsepZdk8CwkRLxh3RKPJMjigmCgyvkSaoDMKAYC3i 20 | YjfUTiJ57UeqoSl0IaOFJ0wfZRFh+UytlDZa 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /examples/order-management-service/order_service/modules/representations/representations.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/http; 18 | 19 | public type OrderItem record {| 20 | string category; 21 | string code; 22 | int qty; 23 | |}; 24 | 25 | public type Order record {| 26 | string id; 27 | string name; 28 | OrderItem[] items; 29 | |}; 30 | 31 | public type UpdateOrder record {| 32 | string name; 33 | OrderItem[] items; 34 | |}; 35 | 36 | public type OrderCreated record {| 37 | *http:Created; 38 | |}; 39 | 40 | public type OrderUpdated record {| 41 | *http:Ok; 42 | |}; 43 | 44 | public type OrderNotFound record {| 45 | *http:BadRequest; 46 | |}; 47 | 48 | public type OrderCanceled record {| 49 | *http:Ok; 50 | |}; 51 | 52 | public enum InventoryOperation { 53 | INCREASE = "increase", 54 | DECREASE = "decrease" 55 | } 56 | -------------------------------------------------------------------------------- /compiler-plugin/src/main/java/io/ballerina/stdlib/jwt/compiler/staticcodeanalyzer/JwtCodeAnalyzer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 WSO2 LLC. (http://www.wso2.com). 3 | * 4 | * WSO2 LLC. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 14 | * OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | package io.ballerina.stdlib.jwt.compiler.staticcodeanalyzer; 20 | 21 | import io.ballerina.compiler.syntax.tree.SyntaxKind; 22 | import io.ballerina.projects.plugins.CodeAnalysisContext; 23 | import io.ballerina.projects.plugins.CodeAnalyzer; 24 | import io.ballerina.scan.Reporter; 25 | 26 | public class JwtCodeAnalyzer extends CodeAnalyzer { 27 | private final Reporter reporter; 28 | 29 | public JwtCodeAnalyzer(Reporter reporter) { 30 | this.reporter = reporter; 31 | } 32 | 33 | @Override 34 | public void init(CodeAnalysisContext codeAnalysisContext) { 35 | codeAnalysisContext.addSyntaxNodeAnalysisTask( 36 | new JwtCipherAlgorithmAnalyzer(reporter), 37 | SyntaxKind.FUNCTION_CALL 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.github/workflows/build-with-bal-test-graalvm.yml: -------------------------------------------------------------------------------- 1 | name: GraalVM Check 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | lang_tag: 7 | description: Branch/Release Tag of the Ballerina Lang 8 | required: true 9 | default: master 10 | lang_version: 11 | description: Ballerina Lang Version (If given ballerina lang buid will be skipped) 12 | required: false 13 | default: '' 14 | native_image_options: 15 | description: Default native-image options 16 | required: false 17 | default: '' 18 | schedule: 19 | - cron: '30 18 * * *' 20 | pull_request: 21 | branches: 22 | - master 23 | types: [ opened, synchronize, reopened, labeled, unlabeled ] 24 | 25 | concurrency: 26 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} 27 | cancel-in-progress: true 28 | 29 | jobs: 30 | call_stdlib_workflow: 31 | name: Run StdLib Workflow 32 | if: ${{ github.event_name != 'schedule' || (github.event_name == 'schedule' && github.repository_owner == 'ballerina-platform') }} 33 | uses: ballerina-platform/ballerina-library/.github/workflows/build-with-bal-test-graalvm-template.yml@main 34 | with: 35 | lang_tag: ${{ inputs.lang_tag }} 36 | lang_version: ${{ inputs.lang_version }} 37 | native_image_options: ${{ inputs.native_image_options }} 38 | additional_windows_build_flags: '-Pdisable=jwks' 39 | -------------------------------------------------------------------------------- /.github/workflows/update_specs.yml: -------------------------------------------------------------------------------- 1 | name: Update Specifications 2 | 3 | env: 4 | SPEC_FOLDER_PATH: 'docs/spec' 5 | 6 | on: 7 | workflow_dispatch: 8 | push: 9 | branches: 10 | - master 11 | paths: 12 | - 'docs/spec/**' 13 | 14 | jobs: 15 | update_specs: 16 | name: Update Specifications 17 | if: github.repository_owner == 'ballerina-platform' 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - name: Checkout Repository 22 | uses: actions/checkout@v3 23 | 24 | - name: Get current date 25 | id: date 26 | run: echo "::set-output name=date::$(date +'%Y-%m-%d')" 27 | 28 | - name: Get Repo Name 29 | id: repo_name 30 | run: | 31 | MODULE=${{ github.event.repository.name }} 32 | echo "::set-output name=short_name::${MODULE##*-}" 33 | 34 | - name: Trigger Workflow 35 | run: | 36 | curl --request POST \ 37 | 'https://api.github.com/repos/ballerina-platform/ballerina-dev-website/dispatches' \ 38 | -H 'Accept: application/vnd.github.v3+json' \ 39 | -H 'Authorization: Bearer ${{ secrets.BALLERINA_BOT_TOKEN }}' \ 40 | --data "{ 41 | \"event_type\": \"update-stdlib-specs\", 42 | \"client_payload\": { 43 | \"module_name\": \"${{ github.event.repository.name }}\", 44 | \"short_name\": \"${{ steps.repo_name.outputs.short_name }}\", 45 | \"file_dir\": \"${{ github.event.repository.name }}/${{ env.SPEC_FOLDER_PATH }}\", 46 | \"release_date\": \"${{ steps.date.outputs.date }}\" 47 | } 48 | }" 49 | -------------------------------------------------------------------------------- /compiler-plugin/src/main/java/io/ballerina/stdlib/jwt/compiler/JwtCompilerPlugin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 WSO2 LLC. (http://www.wso2.com). 3 | * 4 | * WSO2 LLC. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 14 | * OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | package io.ballerina.stdlib.jwt.compiler; 20 | 21 | import io.ballerina.projects.plugins.CompilerPlugin; 22 | import io.ballerina.projects.plugins.CompilerPluginContext; 23 | import io.ballerina.scan.ScannerContext; 24 | import io.ballerina.stdlib.jwt.compiler.staticcodeanalyzer.JwtCodeAnalyzer; 25 | 26 | /** 27 | * jwt compiler plugin. 28 | * 29 | * @since 2.15.1 30 | */ 31 | public class JwtCompilerPlugin extends CompilerPlugin { 32 | 33 | private static final String SCANNER_CONTEXT = "ScannerContext"; 34 | 35 | @Override 36 | public void init(CompilerPluginContext context) { 37 | Object object = context.userData().get(SCANNER_CONTEXT); 38 | if (object instanceof ScannerContext scannerContext) { 39 | context.addCodeAnalyzer(new JwtCodeAnalyzer(scannerContext.getReporter())); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /compiler-plugin/src/main/java/io/ballerina/stdlib/jwt/compiler/staticcodeanalyzer/RuleImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 WSO2 LLC. (http://www.wso2.com). 3 | * 4 | * WSO2 LLC. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 14 | * OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | package io.ballerina.stdlib.jwt.compiler.staticcodeanalyzer; 20 | 21 | import io.ballerina.scan.Rule; 22 | import io.ballerina.scan.RuleKind; 23 | 24 | public class RuleImpl implements Rule { 25 | private final int id; 26 | private final String description; 27 | private final RuleKind kind; 28 | 29 | RuleImpl(int id, String description, RuleKind kind) { 30 | this.id = id; 31 | this.description = description; 32 | this.kind = kind; 33 | } 34 | 35 | @Override 36 | public String id() { 37 | return Integer.toString(this.id); 38 | } 39 | 40 | @Override 41 | public int numericId() { 42 | return this.id; 43 | } 44 | 45 | @Override 46 | public String description() { 47 | return this.description; 48 | } 49 | 50 | @Override 51 | public RuleKind kind() { 52 | return this.kind; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ballerina/tests/client_self_signed_jwt_auth_provider_test.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | // NOTE: All the tokens/credentials used in this test are dummy tokens/credentials and used only for testing purposes. 18 | 19 | import ballerina/test; 20 | 21 | @test:Config {} 22 | isolated function testClientJwtAuthProviderSuccess() returns Error? { 23 | IssuerConfig jwtConfig = { 24 | username: "admin", 25 | issuer: "wso2", 26 | audience: ["ballerina"], 27 | signatureConfig: { 28 | config: { 29 | keyStore: { 30 | path: KEYSTORE_PATH, 31 | password: "ballerina" 32 | }, 33 | keyAlias: "ballerina", 34 | keyPassword: "ballerina" 35 | } 36 | } 37 | }; 38 | ClientSelfSignedJwtAuthProvider jwtAuthProvider = new(jwtConfig); 39 | string result = check jwtAuthProvider.generateToken(); 40 | test:assertTrue(result.startsWith("eyJhbGciOiJSUzI1NiIsICJ0eXAiOiJKV1QifQ")); 41 | } 42 | -------------------------------------------------------------------------------- /compiler-plugin/src/main/java/io/ballerina/stdlib/jwt/compiler/staticcodeanalyzer/JwtRule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 WSO2 LLC. (http://www.wso2.com). 3 | * 4 | * WSO2 LLC. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 14 | * OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | package io.ballerina.stdlib.jwt.compiler.staticcodeanalyzer; 20 | 21 | import io.ballerina.scan.Rule; 22 | 23 | import static io.ballerina.scan.RuleKind.VULNERABILITY; 24 | import static io.ballerina.stdlib.jwt.compiler.staticcodeanalyzer.RuleFactory.createRule; 25 | 26 | public enum JwtRule { 27 | AVOID_WEAK_CIPHER_ALGORITHMS(createRule(1, "Avoid using weak cipher algorithms when signing and " + 28 | "verifying JWTs", VULNERABILITY)); 29 | 30 | private final Rule rule; 31 | 32 | JwtRule(Rule rule) { 33 | this.rule = rule; 34 | } 35 | 36 | public int getId() { 37 | return this.rule.numericId(); 38 | } 39 | 40 | public String getDescription() { 41 | return this.rule.description(); 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "{\"id\":" + this.getId() + ", \"kind\":\"" + this.rule.kind() + "\"," + 47 | " \"description\" : \"" + this.rule.description() + "\"}"; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ballerina/tests/resources/key/private.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCBXKLp9WJUuJko 3 | SfDn3HM2LWVxiHrP11me6D4X2AqoUiCZ1w976q1LuBlzWNhONYwJvHXewUuRmo0d 4 | 4NRp2ma0qjdMN246XWyozpzPfJdvovfiT+V6HnSbMg82ZppfF3e85Cgrg6mSA7Wf 5 | faaHr62SnexseZ9ioNCuRUrW2Zg208l99IItP4K4iGclQlyjs3S6drH7JJk3rv8B 6 | f76wmmoCEAhknOtGLq543dEUIjFgNdIy/GsSY52fiA4LSjUCv27kGn03nayqoZs3 7 | XHqM0qLdt9SPXbYe7l75j0o6Z9tyGFCKXY/zVwiLfRUqDIOfkN5YS4Ohit52Tj7q 8 | oBmvEzD7AgMBAAECggEAXM/F4u23OummmQ1T1kaIMpqnaalt06jCGAywYBMUsmca 9 | FMYDyfg5lVXkjKl1p8crTeD1AHjWawTjskgYnkmf3ocxXXF3mFBnIUX7o7HURLg7 10 | +RcxoUgwiRiFaZZ7szX3JoLbfzzbcHNQ37kavccBVWwQsFMiU3Tlw+LbKwK6/row 11 | LYsQPx7gT4u7hViat4vQDTYcgyjvvFCiek4ndL6O9K49MxIMU678UXB6ia5iUevy 12 | vgEfcYkKQ5EQ38qS3ZwsubPvj4633jvAJRr/hJD8XINZC74kTXeV3BGH2LlpQOEq 13 | kWkOypwYNjnXtt1JO8+Iu6mEXKUoiIBPfGrJ3vDSQQKBgQDmYPc7kfYan/LHjJRv 14 | iE2CwbC26yVA6+BEPQv9z7jChO9Q6cUbGvM8EEVNpC9nmFogkslzJhz55HP84QZL 15 | u3ptU+D96ncq6zkBqxBfRnZG++D36+XRXIwzz3h+g1Nwrl0y0MFbwlkMm3ZqJdd6 16 | pZz1FZGd6zvQftW8m7jPSKHuswKBgQCPv6czFOZR6bI+qCQdaORpe9JGoAduOD+4 17 | YKl96s0eiAKhkGhFCrMd6GJwWRkpNcfwB+J9sMahORbfvwiYanI56h7Vi30DFPRb 18 | m1m8dLkr6z+8bxMxKJaMXIIjy3UDamgDr7QHInNUih2iGvtB8QqZ0aobsB2XIxZg 19 | qESTMcpYmQKBgHSwSqneraQgvgz7FLhFdtUzHDoacr0mfGqz7R37F99XDAyUy+SF 20 | ywvyRdgkwGodjhEPqH/tnyGn6GP+6nxzknhL0xtppkCT8kT5C4rmmsQrknChCL/5 21 | u34GqUaTaDEb8FLrz/SVRRuQpvLvBey2dADjkuVFH//kLoig64P6iyLnAoGBAIlF 22 | g+2L78YZXVXoS1SqbjUtQUigWXgvzunLpQ/Rwb9+MsUGmgwUg6fz2s1eyGBKM3xM 23 | i0VsIsKjOezBCPxD6oDTyk4yvlbLE+7HE5KcBJikNmFD0RgIonu3e6+jA0MXweyD 24 | RW/qviflHRdInNgDzxPE3KVEMX26zAvRpGrMCWdBAoGAdQ5SvX+mAC3cKqoQ9Zal 25 | lSqWoyjfzP5EaVRG8dtoLxbznQGTTvtHXc65/MznX/L9qkWCS6Eb4HH5M3hFNY46 26 | LNIzGQLznE1odwv7H5B8c0/m3DrKTxbh8bYcrR1BW5/nKZNNW7k1O6OjEozvAajK 27 | JQdp3KBU9S8CmBjGrRpJ2qw= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /examples/order-management-service/sts/resources/private.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCBXKLp9WJUuJko 3 | SfDn3HM2LWVxiHrP11me6D4X2AqoUiCZ1w976q1LuBlzWNhONYwJvHXewUuRmo0d 4 | 4NRp2ma0qjdMN246XWyozpzPfJdvovfiT+V6HnSbMg82ZppfF3e85Cgrg6mSA7Wf 5 | faaHr62SnexseZ9ioNCuRUrW2Zg208l99IItP4K4iGclQlyjs3S6drH7JJk3rv8B 6 | f76wmmoCEAhknOtGLq543dEUIjFgNdIy/GsSY52fiA4LSjUCv27kGn03nayqoZs3 7 | XHqM0qLdt9SPXbYe7l75j0o6Z9tyGFCKXY/zVwiLfRUqDIOfkN5YS4Ohit52Tj7q 8 | oBmvEzD7AgMBAAECggEAXM/F4u23OummmQ1T1kaIMpqnaalt06jCGAywYBMUsmca 9 | FMYDyfg5lVXkjKl1p8crTeD1AHjWawTjskgYnkmf3ocxXXF3mFBnIUX7o7HURLg7 10 | +RcxoUgwiRiFaZZ7szX3JoLbfzzbcHNQ37kavccBVWwQsFMiU3Tlw+LbKwK6/row 11 | LYsQPx7gT4u7hViat4vQDTYcgyjvvFCiek4ndL6O9K49MxIMU678UXB6ia5iUevy 12 | vgEfcYkKQ5EQ38qS3ZwsubPvj4633jvAJRr/hJD8XINZC74kTXeV3BGH2LlpQOEq 13 | kWkOypwYNjnXtt1JO8+Iu6mEXKUoiIBPfGrJ3vDSQQKBgQDmYPc7kfYan/LHjJRv 14 | iE2CwbC26yVA6+BEPQv9z7jChO9Q6cUbGvM8EEVNpC9nmFogkslzJhz55HP84QZL 15 | u3ptU+D96ncq6zkBqxBfRnZG++D36+XRXIwzz3h+g1Nwrl0y0MFbwlkMm3ZqJdd6 16 | pZz1FZGd6zvQftW8m7jPSKHuswKBgQCPv6czFOZR6bI+qCQdaORpe9JGoAduOD+4 17 | YKl96s0eiAKhkGhFCrMd6GJwWRkpNcfwB+J9sMahORbfvwiYanI56h7Vi30DFPRb 18 | m1m8dLkr6z+8bxMxKJaMXIIjy3UDamgDr7QHInNUih2iGvtB8QqZ0aobsB2XIxZg 19 | qESTMcpYmQKBgHSwSqneraQgvgz7FLhFdtUzHDoacr0mfGqz7R37F99XDAyUy+SF 20 | ywvyRdgkwGodjhEPqH/tnyGn6GP+6nxzknhL0xtppkCT8kT5C4rmmsQrknChCL/5 21 | u34GqUaTaDEb8FLrz/SVRRuQpvLvBey2dADjkuVFH//kLoig64P6iyLnAoGBAIlF 22 | g+2L78YZXVXoS1SqbjUtQUigWXgvzunLpQ/Rwb9+MsUGmgwUg6fz2s1eyGBKM3xM 23 | i0VsIsKjOezBCPxD6oDTyk4yvlbLE+7HE5KcBJikNmFD0RgIonu3e6+jA0MXweyD 24 | RW/qviflHRdInNgDzxPE3KVEMX26zAvRpGrMCWdBAoGAdQ5SvX+mAC3cKqoQ9Zal 25 | lSqWoyjfzP5EaVRG8dtoLxbznQGTTvtHXc65/MznX/L9qkWCS6Eb4HH5M3hFNY46 26 | LNIzGQLznE1odwv7H5B8c0/m3DrKTxbh8bYcrR1BW5/nKZNNW7k1O6OjEozvAajK 27 | JQdp3KBU9S8CmBjGrRpJ2qw= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /load-tests/order_management_service/src/resources/sts/private.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCBXKLp9WJUuJko 3 | SfDn3HM2LWVxiHrP11me6D4X2AqoUiCZ1w976q1LuBlzWNhONYwJvHXewUuRmo0d 4 | 4NRp2ma0qjdMN246XWyozpzPfJdvovfiT+V6HnSbMg82ZppfF3e85Cgrg6mSA7Wf 5 | faaHr62SnexseZ9ioNCuRUrW2Zg208l99IItP4K4iGclQlyjs3S6drH7JJk3rv8B 6 | f76wmmoCEAhknOtGLq543dEUIjFgNdIy/GsSY52fiA4LSjUCv27kGn03nayqoZs3 7 | XHqM0qLdt9SPXbYe7l75j0o6Z9tyGFCKXY/zVwiLfRUqDIOfkN5YS4Ohit52Tj7q 8 | oBmvEzD7AgMBAAECggEAXM/F4u23OummmQ1T1kaIMpqnaalt06jCGAywYBMUsmca 9 | FMYDyfg5lVXkjKl1p8crTeD1AHjWawTjskgYnkmf3ocxXXF3mFBnIUX7o7HURLg7 10 | +RcxoUgwiRiFaZZ7szX3JoLbfzzbcHNQ37kavccBVWwQsFMiU3Tlw+LbKwK6/row 11 | LYsQPx7gT4u7hViat4vQDTYcgyjvvFCiek4ndL6O9K49MxIMU678UXB6ia5iUevy 12 | vgEfcYkKQ5EQ38qS3ZwsubPvj4633jvAJRr/hJD8XINZC74kTXeV3BGH2LlpQOEq 13 | kWkOypwYNjnXtt1JO8+Iu6mEXKUoiIBPfGrJ3vDSQQKBgQDmYPc7kfYan/LHjJRv 14 | iE2CwbC26yVA6+BEPQv9z7jChO9Q6cUbGvM8EEVNpC9nmFogkslzJhz55HP84QZL 15 | u3ptU+D96ncq6zkBqxBfRnZG++D36+XRXIwzz3h+g1Nwrl0y0MFbwlkMm3ZqJdd6 16 | pZz1FZGd6zvQftW8m7jPSKHuswKBgQCPv6czFOZR6bI+qCQdaORpe9JGoAduOD+4 17 | YKl96s0eiAKhkGhFCrMd6GJwWRkpNcfwB+J9sMahORbfvwiYanI56h7Vi30DFPRb 18 | m1m8dLkr6z+8bxMxKJaMXIIjy3UDamgDr7QHInNUih2iGvtB8QqZ0aobsB2XIxZg 19 | qESTMcpYmQKBgHSwSqneraQgvgz7FLhFdtUzHDoacr0mfGqz7R37F99XDAyUy+SF 20 | ywvyRdgkwGodjhEPqH/tnyGn6GP+6nxzknhL0xtppkCT8kT5C4rmmsQrknChCL/5 21 | u34GqUaTaDEb8FLrz/SVRRuQpvLvBey2dADjkuVFH//kLoig64P6iyLnAoGBAIlF 22 | g+2L78YZXVXoS1SqbjUtQUigWXgvzunLpQ/Rwb9+MsUGmgwUg6fz2s1eyGBKM3xM 23 | i0VsIsKjOezBCPxD6oDTyk4yvlbLE+7HE5KcBJikNmFD0RgIonu3e6+jA0MXweyD 24 | RW/qviflHRdInNgDzxPE3KVEMX26zAvRpGrMCWdBAoGAdQ5SvX+mAC3cKqoQ9Zal 25 | lSqWoyjfzP5EaVRG8dtoLxbznQGTTvtHXc65/MznX/L9qkWCS6Eb4HH5M3hFNY46 26 | LNIzGQLznE1odwv7H5B8c0/m3DrKTxbh8bYcrR1BW5/nKZNNW7k1O6OjEozvAajK 27 | JQdp3KBU9S8CmBjGrRpJ2qw= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /examples/order-management-service/inventory_service/resources/private.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCBXKLp9WJUuJko 3 | SfDn3HM2LWVxiHrP11me6D4X2AqoUiCZ1w976q1LuBlzWNhONYwJvHXewUuRmo0d 4 | 4NRp2ma0qjdMN246XWyozpzPfJdvovfiT+V6HnSbMg82ZppfF3e85Cgrg6mSA7Wf 5 | faaHr62SnexseZ9ioNCuRUrW2Zg208l99IItP4K4iGclQlyjs3S6drH7JJk3rv8B 6 | f76wmmoCEAhknOtGLq543dEUIjFgNdIy/GsSY52fiA4LSjUCv27kGn03nayqoZs3 7 | XHqM0qLdt9SPXbYe7l75j0o6Z9tyGFCKXY/zVwiLfRUqDIOfkN5YS4Ohit52Tj7q 8 | oBmvEzD7AgMBAAECggEAXM/F4u23OummmQ1T1kaIMpqnaalt06jCGAywYBMUsmca 9 | FMYDyfg5lVXkjKl1p8crTeD1AHjWawTjskgYnkmf3ocxXXF3mFBnIUX7o7HURLg7 10 | +RcxoUgwiRiFaZZ7szX3JoLbfzzbcHNQ37kavccBVWwQsFMiU3Tlw+LbKwK6/row 11 | LYsQPx7gT4u7hViat4vQDTYcgyjvvFCiek4ndL6O9K49MxIMU678UXB6ia5iUevy 12 | vgEfcYkKQ5EQ38qS3ZwsubPvj4633jvAJRr/hJD8XINZC74kTXeV3BGH2LlpQOEq 13 | kWkOypwYNjnXtt1JO8+Iu6mEXKUoiIBPfGrJ3vDSQQKBgQDmYPc7kfYan/LHjJRv 14 | iE2CwbC26yVA6+BEPQv9z7jChO9Q6cUbGvM8EEVNpC9nmFogkslzJhz55HP84QZL 15 | u3ptU+D96ncq6zkBqxBfRnZG++D36+XRXIwzz3h+g1Nwrl0y0MFbwlkMm3ZqJdd6 16 | pZz1FZGd6zvQftW8m7jPSKHuswKBgQCPv6czFOZR6bI+qCQdaORpe9JGoAduOD+4 17 | YKl96s0eiAKhkGhFCrMd6GJwWRkpNcfwB+J9sMahORbfvwiYanI56h7Vi30DFPRb 18 | m1m8dLkr6z+8bxMxKJaMXIIjy3UDamgDr7QHInNUih2iGvtB8QqZ0aobsB2XIxZg 19 | qESTMcpYmQKBgHSwSqneraQgvgz7FLhFdtUzHDoacr0mfGqz7R37F99XDAyUy+SF 20 | ywvyRdgkwGodjhEPqH/tnyGn6GP+6nxzknhL0xtppkCT8kT5C4rmmsQrknChCL/5 21 | u34GqUaTaDEb8FLrz/SVRRuQpvLvBey2dADjkuVFH//kLoig64P6iyLnAoGBAIlF 22 | g+2L78YZXVXoS1SqbjUtQUigWXgvzunLpQ/Rwb9+MsUGmgwUg6fz2s1eyGBKM3xM 23 | i0VsIsKjOezBCPxD6oDTyk4yvlbLE+7HE5KcBJikNmFD0RgIonu3e6+jA0MXweyD 24 | RW/qviflHRdInNgDzxPE3KVEMX26zAvRpGrMCWdBAoGAdQ5SvX+mAC3cKqoQ9Zal 25 | lSqWoyjfzP5EaVRG8dtoLxbznQGTTvtHXc65/MznX/L9qkWCS6Eb4HH5M3hFNY46 26 | LNIzGQLznE1odwv7H5B8c0/m3DrKTxbh8bYcrR1BW5/nKZNNW7k1O6OjEozvAajK 27 | JQdp3KBU9S8CmBjGrRpJ2qw= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /examples/order-management-service/order_service/resources/private.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCBXKLp9WJUuJko 3 | SfDn3HM2LWVxiHrP11me6D4X2AqoUiCZ1w976q1LuBlzWNhONYwJvHXewUuRmo0d 4 | 4NRp2ma0qjdMN246XWyozpzPfJdvovfiT+V6HnSbMg82ZppfF3e85Cgrg6mSA7Wf 5 | faaHr62SnexseZ9ioNCuRUrW2Zg208l99IItP4K4iGclQlyjs3S6drH7JJk3rv8B 6 | f76wmmoCEAhknOtGLq543dEUIjFgNdIy/GsSY52fiA4LSjUCv27kGn03nayqoZs3 7 | XHqM0qLdt9SPXbYe7l75j0o6Z9tyGFCKXY/zVwiLfRUqDIOfkN5YS4Ohit52Tj7q 8 | oBmvEzD7AgMBAAECggEAXM/F4u23OummmQ1T1kaIMpqnaalt06jCGAywYBMUsmca 9 | FMYDyfg5lVXkjKl1p8crTeD1AHjWawTjskgYnkmf3ocxXXF3mFBnIUX7o7HURLg7 10 | +RcxoUgwiRiFaZZ7szX3JoLbfzzbcHNQ37kavccBVWwQsFMiU3Tlw+LbKwK6/row 11 | LYsQPx7gT4u7hViat4vQDTYcgyjvvFCiek4ndL6O9K49MxIMU678UXB6ia5iUevy 12 | vgEfcYkKQ5EQ38qS3ZwsubPvj4633jvAJRr/hJD8XINZC74kTXeV3BGH2LlpQOEq 13 | kWkOypwYNjnXtt1JO8+Iu6mEXKUoiIBPfGrJ3vDSQQKBgQDmYPc7kfYan/LHjJRv 14 | iE2CwbC26yVA6+BEPQv9z7jChO9Q6cUbGvM8EEVNpC9nmFogkslzJhz55HP84QZL 15 | u3ptU+D96ncq6zkBqxBfRnZG++D36+XRXIwzz3h+g1Nwrl0y0MFbwlkMm3ZqJdd6 16 | pZz1FZGd6zvQftW8m7jPSKHuswKBgQCPv6czFOZR6bI+qCQdaORpe9JGoAduOD+4 17 | YKl96s0eiAKhkGhFCrMd6GJwWRkpNcfwB+J9sMahORbfvwiYanI56h7Vi30DFPRb 18 | m1m8dLkr6z+8bxMxKJaMXIIjy3UDamgDr7QHInNUih2iGvtB8QqZ0aobsB2XIxZg 19 | qESTMcpYmQKBgHSwSqneraQgvgz7FLhFdtUzHDoacr0mfGqz7R37F99XDAyUy+SF 20 | ywvyRdgkwGodjhEPqH/tnyGn6GP+6nxzknhL0xtppkCT8kT5C4rmmsQrknChCL/5 21 | u34GqUaTaDEb8FLrz/SVRRuQpvLvBey2dADjkuVFH//kLoig64P6iyLnAoGBAIlF 22 | g+2L78YZXVXoS1SqbjUtQUigWXgvzunLpQ/Rwb9+MsUGmgwUg6fz2s1eyGBKM3xM 23 | i0VsIsKjOezBCPxD6oDTyk4yvlbLE+7HE5KcBJikNmFD0RgIonu3e6+jA0MXweyD 24 | RW/qviflHRdInNgDzxPE3KVEMX26zAvRpGrMCWdBAoGAdQ5SvX+mAC3cKqoQ9Zal 25 | lSqWoyjfzP5EaVRG8dtoLxbznQGTTvtHXc65/MznX/L9qkWCS6Eb4HH5M3hFNY46 26 | LNIzGQLznE1odwv7H5B8c0/m3DrKTxbh8bYcrR1BW5/nKZNNW7k1O6OjEozvAajK 27 | JQdp3KBU9S8CmBjGrRpJ2qw= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /load-tests/order_management_service/src/resources/order_service/private.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCBXKLp9WJUuJko 3 | SfDn3HM2LWVxiHrP11me6D4X2AqoUiCZ1w976q1LuBlzWNhONYwJvHXewUuRmo0d 4 | 4NRp2ma0qjdMN246XWyozpzPfJdvovfiT+V6HnSbMg82ZppfF3e85Cgrg6mSA7Wf 5 | faaHr62SnexseZ9ioNCuRUrW2Zg208l99IItP4K4iGclQlyjs3S6drH7JJk3rv8B 6 | f76wmmoCEAhknOtGLq543dEUIjFgNdIy/GsSY52fiA4LSjUCv27kGn03nayqoZs3 7 | XHqM0qLdt9SPXbYe7l75j0o6Z9tyGFCKXY/zVwiLfRUqDIOfkN5YS4Ohit52Tj7q 8 | oBmvEzD7AgMBAAECggEAXM/F4u23OummmQ1T1kaIMpqnaalt06jCGAywYBMUsmca 9 | FMYDyfg5lVXkjKl1p8crTeD1AHjWawTjskgYnkmf3ocxXXF3mFBnIUX7o7HURLg7 10 | +RcxoUgwiRiFaZZ7szX3JoLbfzzbcHNQ37kavccBVWwQsFMiU3Tlw+LbKwK6/row 11 | LYsQPx7gT4u7hViat4vQDTYcgyjvvFCiek4ndL6O9K49MxIMU678UXB6ia5iUevy 12 | vgEfcYkKQ5EQ38qS3ZwsubPvj4633jvAJRr/hJD8XINZC74kTXeV3BGH2LlpQOEq 13 | kWkOypwYNjnXtt1JO8+Iu6mEXKUoiIBPfGrJ3vDSQQKBgQDmYPc7kfYan/LHjJRv 14 | iE2CwbC26yVA6+BEPQv9z7jChO9Q6cUbGvM8EEVNpC9nmFogkslzJhz55HP84QZL 15 | u3ptU+D96ncq6zkBqxBfRnZG++D36+XRXIwzz3h+g1Nwrl0y0MFbwlkMm3ZqJdd6 16 | pZz1FZGd6zvQftW8m7jPSKHuswKBgQCPv6czFOZR6bI+qCQdaORpe9JGoAduOD+4 17 | YKl96s0eiAKhkGhFCrMd6GJwWRkpNcfwB+J9sMahORbfvwiYanI56h7Vi30DFPRb 18 | m1m8dLkr6z+8bxMxKJaMXIIjy3UDamgDr7QHInNUih2iGvtB8QqZ0aobsB2XIxZg 19 | qESTMcpYmQKBgHSwSqneraQgvgz7FLhFdtUzHDoacr0mfGqz7R37F99XDAyUy+SF 20 | ywvyRdgkwGodjhEPqH/tnyGn6GP+6nxzknhL0xtppkCT8kT5C4rmmsQrknChCL/5 21 | u34GqUaTaDEb8FLrz/SVRRuQpvLvBey2dADjkuVFH//kLoig64P6iyLnAoGBAIlF 22 | g+2L78YZXVXoS1SqbjUtQUigWXgvzunLpQ/Rwb9+MsUGmgwUg6fz2s1eyGBKM3xM 23 | i0VsIsKjOezBCPxD6oDTyk4yvlbLE+7HE5KcBJikNmFD0RgIonu3e6+jA0MXweyD 24 | RW/qviflHRdInNgDzxPE3KVEMX26zAvRpGrMCWdBAoGAdQ5SvX+mAC3cKqoQ9Zal 25 | lSqWoyjfzP5EaVRG8dtoLxbznQGTTvtHXc65/MznX/L9qkWCS6Eb4HH5M3hFNY46 26 | LNIzGQLznE1odwv7H5B8c0/m3DrKTxbh8bYcrR1BW5/nKZNNW7k1O6OjEozvAajK 27 | JQdp3KBU9S8CmBjGrRpJ2qw= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /ballerina/tests/resources/key/encryptedPrivate.key: -------------------------------------------------------------------------------- 1 | -----BEGIN ENCRYPTED PRIVATE KEY----- 2 | MIIE6jAcBgoqhkiG9w0BDAEEMA4ECJ1AVEOFT+s1AgIFDASCBMiHG70PvakbuVha 3 | 9JBcXWzje1B30LItVdpiETbu5LNG3v0IIX7DmRQCBJxXkkCeZ3Bcw/A1cjkGIR0j 4 | 6P1AyUAlZBU9+GcCF2BcpyaMOxNVQuHUZA9//fnlfO6KMTdIg9b/MutH++bLSoh9 5 | JkHivtgEQVEAu79NpTdKh9XklIWUBe5gU4OteBL0/feKgB6NFeYm7vNb8b486jSB 6 | oXWC7l8Y5VoU4grhDT9dL1PQqLQvIRWTnB1BhUE9OquWrRRaGE7WeWABzzFhHB4X 7 | DdHABqsNE9qeEGHrUBKfjfg6+8GIEjjJcpJt9wGBi4yHbUaao7xueu0aA9nVVo75 8 | 6PJOwcJqoPhwkDFpAYADLJTVxh8EqbaihSe3HVqjsF3au5nu338VbJu8VZ2rAgTv 9 | V2IxsV/BWrv8RIbt/EINz82uU0umbhpvUakMjZGD01NCvx6mshfKA3zIEac7apmc 10 | 6wwoqh42sEHk3i7H/xQhuS32ACzRDwXzXYf2f8CZVrSk7Ajc4w4mwY5YylCmn+P5 11 | YK6fRCaJAN2J4CGvIut3LvjGX8pNuu9msFMrFjbITL/xeUzIrJoE1C9O2V1dQ5LU 12 | O4wVFGKHkFpBBzxxDQwRbYn66EbL4/dCwtIkj7kncz7Y+qYBi+Voe81RSL1mBtR/ 13 | nOQPIHNOLSUvIPA2aj4ufcdSrbXapMfrTOiO+EUuBOGE6FncaGm3hfKnPa1C4fuZ 14 | yokvpEMNOUs5eJjSC0tcP+zibTkiMHy2vhvrI3xme6oIFG7Nvxi2MxcWolUNmXlk 15 | UVGyJJurL9QhKfjGSIuEZgJd7/PrDK0gzEQBMS/10BUPRe1pE+05GEOvJBbhw1Bt 16 | 6b3LjbVtNnR/2/G3I9LlwRLac4IHlU/JMy58E3Uxim0/rJJ7uGGbXsxdwFS4968I 17 | /ekA1PcZgvNet60rMYxSXz6QPwYnvM94gFD6I5PgUhJJPeQZiw9kFLBqKsUjBrjY 18 | jFAcTVW6sWEo1CMsUC94gwvHvpOLtPHjik/iPAfHXeGQ9baTaArMAJiQO+0h9O3Y 19 | 0rXM8gEz4zAID3N4neVjjMda7C/2avAfJRoBxYAFMcuMNTta4mMmXOCs+M6x45GI 20 | w/KPAZbsRTcRWLjU5QguqP6eAxSghHITruW0HEqscT20K3cRWXXDEOr8ZqId90xY 21 | PQo5ZEvaJLGgxN4Q7CUPJJ1mKHruTmMm+UOxIuXRR2eaqsJDnLovmu4jGOSLVYV4 22 | FFJ7PxD+tx9b8WS8tckS/Pjm2PcUAJz3I6/DFc3iKwoeCXBIG/djwWfabRcoGb0U 23 | Sq8tiqvCjT/LuubpQgKhkEnxWCBRR+Qns4quJJ2Kead/g8E19cUxolGPTW9jr2GF 24 | 1XWuGytK0PqUOIMsuJkhWN3dErFmftcfmfhqFFulOvE7eWr+ZToyFESmNK+MNnAL 25 | mmZFOt7zTNwooQkzpVohLtKPwe3k71dhhsK3gQBoscKQFnMQSmKLM7c4A6HPcq20 26 | XFtRM30O5cSO2jLZ+sAcJS81uIrcPWWf8yY5lMZ1irHFri6jnwcv7Sbe3rqZJFer 27 | 9ckVz4rq399UHOyujNT2gjIaFnEwmHfM0yaZ0dYG2JU/jXdbqsTLKsHSOZsrkkL5 28 | 9BaHO8wfzVx/CnQfP6M= 29 | -----END ENCRYPTED PRIVATE KEY----- 30 | -------------------------------------------------------------------------------- /examples/order-management-service/inventory_service/inventory_service.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/http; 18 | import inventory_service.inventory as inv; 19 | import inventory_service.representations as rep; 20 | 21 | listener http:Listener inventoryEP = new (9091, 22 | secureSocket = { 23 | key: { 24 | certFile: "./resources/public.crt", 25 | keyFile: "./resources/private.key" 26 | }, 27 | mutualSsl: { 28 | verifyClient: http:REQUIRE, 29 | cert: "./resources/public.crt" 30 | } 31 | } 32 | ); 33 | 34 | service /inventory on inventoryEP { 35 | resource function put decrease(@http:Payload rep:OrderItem[] orderItems) returns rep:InventoryUpdated { 36 | foreach rep:OrderItem orderItem in orderItems { 37 | inv:decreaseQty(orderItem.category, orderItem.code, orderItem.qty); 38 | } 39 | return {}; 40 | } 41 | 42 | resource function put increase(@http:Payload rep:OrderItem[] orderItems) returns rep:InventoryUpdated { 43 | foreach rep:OrderItem orderItem in orderItems { 44 | inv:increaseQty(orderItem.category, orderItem.code, orderItem.qty); 45 | } 46 | return {}; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * 6 | * Detailed information about configuring a multi-project build in Gradle can be found 7 | * in the user manual at https://docs.gradle.org/6.3/userguide/multi_project_builds.html 8 | */ 9 | 10 | pluginManagement { 11 | plugins { 12 | id "com.github.spotbugs" version "${spotbugsPluginVersion}" 13 | id "com.github.johnrengelman.shadow" version "${shadowJarPluginVersion}" 14 | id "de.undercouch.download" version "${downloadPluginVersion}" 15 | id "net.researchgate.release" version "${releasePluginVersion}" 16 | id "io.ballerina.plugin" version "${ballerinaGradlePluginVersion}" 17 | } 18 | 19 | repositories { 20 | gradlePluginPortal() 21 | maven { 22 | url = 'https://maven.pkg.github.com/ballerina-platform/*' 23 | credentials { 24 | username System.getenv("packageUser") 25 | password System.getenv("packagePAT") 26 | } 27 | } 28 | } 29 | } 30 | 31 | plugins { 32 | id "com.gradle.enterprise" version "3.13.2" 33 | } 34 | 35 | rootProject.name = 'jwt' 36 | 37 | include ':checkstyle' 38 | include ':jwt-native' 39 | include ':jwt-ballerina' 40 | include ':jwt-compiler-plugin' 41 | include ':jwt-compiler-plugin-tests' 42 | 43 | project(':checkstyle').projectDir = file("build-config${File.separator}checkstyle") 44 | project(':jwt-native').projectDir = file('native') 45 | project(':jwt-ballerina').projectDir = file('ballerina') 46 | project(':jwt-compiler-plugin').projectDir = file('compiler-plugin') 47 | project(':jwt-compiler-plugin-tests').projectDir = file('compiler-plugin-tests') 48 | 49 | gradleEnterprise { 50 | buildScan { 51 | termsOfServiceUrl = 'https://gradle.com/terms-of-service' 52 | termsOfServiceAgree = 'yes' 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /load-tests/order_management_service/src/order_service.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/cache; 18 | import ballerina/http; 19 | 20 | final http:JwtValidatorConfig config = { 21 | issuer: "wso2", 22 | audience: "ballerina", 23 | signatureConfig: { 24 | jwksConfig: { 25 | url: "https://localhost:9445/oauth2/jwks", 26 | clientConfig: { 27 | secureSocket: { 28 | cert: "./resources/order_service/public.crt" 29 | } 30 | }, 31 | cacheConfig: { 32 | capacity: 10, 33 | evictionFactor: 0.25, 34 | evictionPolicy: cache:LRU, 35 | defaultMaxAge: -1 36 | } 37 | } 38 | }, 39 | cacheConfig: { 40 | capacity: 10, 41 | evictionFactor: 0.25, 42 | evictionPolicy: cache:LRU, 43 | defaultMaxAge: -1 44 | } 45 | }; 46 | 47 | listener http:Listener orderEP = new (9090, 48 | secureSocket = { 49 | key: { 50 | certFile: "./resources/order_service/public.crt", 51 | keyFile: "./resources/order_service/private.key" 52 | } 53 | } 54 | ); 55 | 56 | isolated service /'order on orderEP { 57 | 58 | @http:ResourceConfig { 59 | auth: [ 60 | { 61 | jwtValidatorConfig: config, 62 | scopes: "add_order" 63 | } 64 | ] 65 | } 66 | isolated resource function post .(@http:Payload json payload) returns json { 67 | return payload; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /native/src/main/java/io/ballerina/stdlib/jwt/JwtUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | package io.ballerina.stdlib.jwt; 20 | 21 | import io.ballerina.runtime.api.creators.ErrorCreator; 22 | import io.ballerina.runtime.api.creators.ValueCreator; 23 | import io.ballerina.runtime.api.utils.StringUtils; 24 | import io.ballerina.runtime.api.values.BArray; 25 | import io.ballerina.runtime.api.values.BError; 26 | import io.ballerina.runtime.api.values.BString; 27 | 28 | import java.nio.charset.StandardCharsets; 29 | import java.util.Base64; 30 | 31 | /** 32 | * JWT related utility functions. 33 | * 34 | * @since 2.0.0 35 | */ 36 | public class JwtUtils { 37 | 38 | private JwtUtils() {} 39 | 40 | public static BString encodeBase64Url(BArray input) { 41 | byte[] encodedValue = Base64.getUrlEncoder().withoutPadding().encode(input.getBytes()); 42 | return StringUtils.fromString(new String(encodedValue, StandardCharsets.ISO_8859_1)); 43 | } 44 | 45 | public static Object decodeBase64Url(BString input) { 46 | try { 47 | byte[] output = Base64.getUrlDecoder().decode(input.getValue()); 48 | return ValueCreator.createArrayValue(output); 49 | } catch (IllegalArgumentException e) { 50 | return createError("Input is not a valid Base64 URL encoded value. " + e.getMessage()); 51 | } 52 | } 53 | 54 | public static BError createError(String errMsg) { 55 | return ErrorCreator.createDistinctError(JwtConstants.JWT_ERROR_TYPE, ModuleUtils.getModule(), 56 | StringUtils.fromString(errMsg)); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ballerina/client_self_signed_jwt_auth_provider.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | # Represents the client JWT Auth provider, which is used to authenticate with an external endpoint by issuing a 18 | # self-signed JWT against the provided JWT issuer configurations. 19 | # ```ballerina 20 | # jwt:ClientSelfSignedJwtAuthProvider provider = new({ 21 | # issuer: "wso2", 22 | # audience: "ballerina", 23 | # keyStoreConfig: { 24 | # keyAlias: "ballerina", 25 | # keyPassword: "ballerina", 26 | # keyStore: { 27 | # path: "/path/to/keystore.p12", 28 | # password: "ballerina" 29 | # } 30 | # } 31 | # }); 32 | # ``` 33 | public isolated class ClientSelfSignedJwtAuthProvider { 34 | 35 | private final IssuerConfig & readonly issuerConfig; 36 | 37 | # Provides authentication based on the provided JWT configurations. 38 | # 39 | # + issuerConfig - JWT issuer configurations 40 | public isolated function init(IssuerConfig issuerConfig) { 41 | self.issuerConfig = issuerConfig.cloneReadOnly(); 42 | } 43 | 44 | # Issues a self-signed JWT for authentication. 45 | # ```ballerina 46 | # string token = check provider.generateToken(); 47 | # ``` 48 | # 49 | # + return - Generated token or else a `jwt:Error` if an error occurred 50 | public isolated function generateToken() returns string|Error { 51 | string|Error result = issue(self.issuerConfig); 52 | if result is string { 53 | return result; 54 | } else { 55 | return prepareError("Failed to generate a self-signed JWT.", result); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ballerina/README.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | 3 | This module provides a framework for authentication/authorization with JWTs and generation/validation of JWTs as specified in the [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519), [RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515), and [RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517). 4 | 5 | JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure enabling the claims to be signed digitally or protecting the integrity with a Message Authentication Code(MAC) and/or encrypted. 6 | 7 | The Ballerina `jwt` module facilitates auth providers that are to be used by the clients and listeners of different protocol connectors. Also, it provides the APIs for issuing a self-signed JWT and validating a JWT. 8 | 9 | ### Listener JWT Auth provider 10 | 11 | Represents the listener JWT Auth provider, which is used to authenticate the provided credentials (JWT) against the provided JWT validator configurations. 12 | 13 | ### Client JWT Auth provider 14 | 15 | Represents the client JWT Auth provider, which is used to authenticate with an external endpoint by issuing a self-signed JWT against the provided JWT issuer configurations. 16 | 17 | ### JWT issuer 18 | 19 | A self-signed JWT can be issued with the provided configurations using this API as follows: 20 | 21 | ```ballerina 22 | jwt:IssuerConfig issuerConfig = { 23 | username: "ballerina", 24 | issuer: "wso2", 25 | audience: "vEwzbcasJVQm1jVYHUHCjhxZ4tYa", 26 | expTime: 3600, 27 | signatureConfig: { 28 | config: { 29 | keyFile: "/path/to/private.key" 30 | } 31 | } 32 | }; 33 | 34 | string jwt = check jwt:issue(issuerConfig); 35 | ``` 36 | 37 | ### JWT validator 38 | 39 | A JWT can be validated with the provided configurations using the API as follows: 40 | 41 | ```ballerina 42 | string jwt = "eyJ0eXAiOiJKV1QiLA0KI[...omitted for brevity...]mB92K27uhbwW1gFWFOEjXk"; 43 | 44 | jwt:ValidatorConfig validatorConfig = { 45 | issuer: "wso2", 46 | audience: "vEwzbcasJVQm1jVYHUHCjhxZ4tYa", 47 | clockSkew: 60, 48 | signatureConfig: { 49 | certFile: "/path/to/public.crt" 50 | } 51 | }; 52 | 53 | jwt:Payload result = check jwt:validate(jwt, validatorConfig); 54 | ``` 55 | -------------------------------------------------------------------------------- /native/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | plugins { 19 | id 'java' 20 | id 'checkstyle' 21 | id 'com.github.spotbugs' 22 | } 23 | 24 | description = 'Ballerina - JWT Java Utils' 25 | 26 | dependencies { 27 | checkstyle project(':checkstyle') 28 | checkstyle "com.puppycrawl.tools:checkstyle:${checkstylePluginVersion}" 29 | 30 | implementation group: 'org.ballerinalang', name: 'ballerina-runtime', version: "${ballerinaLangVersion}" 31 | implementation group: 'io.ballerina.stdlib', name: 'crypto-native', version: "${stdlibCryptoVersion}" 32 | } 33 | 34 | checkstyle { 35 | toolVersion '7.8.2' 36 | configFile rootProject.file("build-config/checkstyle/build/checkstyle.xml") 37 | configProperties = ["suppressionFile" : file("${rootDir}/build-config/checkstyle/build/suppressions.xml")] 38 | } 39 | 40 | checkstyleMain.dependsOn(":checkstyle:downloadCheckstyleRuleFiles") 41 | 42 | spotbugsMain { 43 | def classLoader = plugins["com.github.spotbugs"].class.classLoader 44 | def SpotBugsConfidence = classLoader.findLoadedClass("com.github.spotbugs.snom.Confidence") 45 | def SpotBugsEffort = classLoader.findLoadedClass("com.github.spotbugs.snom.Effort") 46 | effort = SpotBugsEffort.MAX 47 | reportLevel = SpotBugsConfidence.LOW 48 | reportsDir = file("$project.buildDir/reports/spotbugs") 49 | reports { 50 | html.enabled true 51 | text.enabled = true 52 | } 53 | def excludeFile = file("${rootDir}/spotbugs-exclude.xml") 54 | if (excludeFile.exists()) { 55 | excludeFilter = excludeFile 56 | } 57 | } 58 | 59 | def excludePattern = '**/module-info.java' 60 | tasks.withType(Checkstyle) { 61 | exclude excludePattern 62 | } 63 | 64 | compileJava { 65 | doFirst { 66 | options.compilerArgs = [ 67 | '--module-path', classpath.asPath, 68 | ] 69 | classpath = files() 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /examples/order-management-service/sts/sts.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/http; 18 | 19 | listener http:Listener sts = new(9445, 20 | secureSocket = { 21 | key: { 22 | certFile: "./resources/public.crt", 23 | keyFile: "./resources/private.key" 24 | } 25 | } 26 | ); 27 | 28 | service /oauth2 on sts { 29 | 30 | // This JWKs endpoint respond with a JSON object that represents a set of JWKs. 31 | // https://tools.ietf.org/html/rfc7517#section-5 32 | resource function get jwks() returns json { 33 | return { 34 | "keys": [{ 35 | "kty": "EC", 36 | "crv": "P-256", 37 | "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", 38 | "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", 39 | "use": "enc", 40 | "kid": "1" 41 | }, 42 | { 43 | "kty": "RSA", 44 | "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", 45 | "e": "AQAB", 46 | "alg": "RS256", 47 | "kid": "2011-04-29" 48 | }, 49 | { 50 | "kty": "RSA", 51 | "e": "AQAB", 52 | "use": "sig", 53 | "kid": "NTAxZmMxNDMyZDg3MTU1ZGM0MzEzODJhZWI4NDNlZDU1OGFkNjFiMQ", 54 | "alg": "RS256", 55 | "n": "AIFcoun1YlS4mShJ8OfcczYtZXGIes_XWZ7oPhfYCqhSIJnXD3vqrUu4GXNY2E41jAm8dd7BS5GajR3g1GnaZrSqN0w3bjpdbKjOnM98l2-i9-JP5XoedJsyDzZmml8Xd7zkKCuDqZIDtZ99poevrZKd7Gx5n2Kg0K5FStbZmDbTyX30gi0_griIZyVCXKOzdLp2sfskmTeu_wF_vrCaagIQCGSc60Yurnjd0RQiMWA10jL8axJjnZ-IDgtKNQK_buQafTedrKqhmzdceozSot231I9dth7uXvmPSjpn23IYUIpdj_NXCIt9FSoMg5-Q3lhLg6GK3nZOPuqgGa8TMPs=" 56 | }] 57 | }; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /load-tests/order_management_service/src/sts.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/http; 18 | 19 | listener http:Listener sts = new(9445, 20 | secureSocket = { 21 | key: { 22 | certFile: "./resources/sts/public.crt", 23 | keyFile: "./resources/sts/private.key" 24 | } 25 | } 26 | ); 27 | 28 | isolated service /oauth2 on sts { 29 | 30 | // This JWKs endpoint respond with a JSON object that represents a set of JWKs. 31 | // https://tools.ietf.org/html/rfc7517#section-5 32 | isolated resource function get jwks() returns json { 33 | return { 34 | "keys": [{ 35 | "kty": "EC", 36 | "crv": "P-256", 37 | "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", 38 | "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", 39 | "use": "enc", 40 | "kid": "1" 41 | }, 42 | { 43 | "kty": "RSA", 44 | "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", 45 | "e": "AQAB", 46 | "alg": "RS256", 47 | "kid": "2011-04-29" 48 | }, 49 | { 50 | "kty": "RSA", 51 | "e": "AQAB", 52 | "use": "sig", 53 | "kid": "NTAxZmMxNDMyZDg3MTU1ZGM0MzEzODJhZWI4NDNlZDU1OGFkNjFiMQ", 54 | "alg": "RS256", 55 | "n": "AIFcoun1YlS4mShJ8OfcczYtZXGIes_XWZ7oPhfYCqhSIJnXD3vqrUu4GXNY2E41jAm8dd7BS5GajR3g1GnaZrSqN0w3bjpdbKjOnM98l2-i9-JP5XoedJsyDzZmml8Xd7zkKCuDqZIDtZ99poevrZKd7Gx5n2Kg0K5FStbZmDbTyX30gi0_griIZyVCXKOzdLp2sfskmTeu_wF_vrCaagIQCGSc60Yurnjd0RQiMWA10jL8axJjnZ-IDgtKNQK_buQafTedrKqhmzdceozSot231I9dth7uXvmPSjpn23IYUIpdj_NXCIt9FSoMg5-Q3lhLg6GK3nZOPuqgGa8TMPs=" 56 | }] 57 | }; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /native/src/main/java/io/ballerina/stdlib/jwt/JwtConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | package io.ballerina.stdlib.jwt; 20 | 21 | import io.ballerina.runtime.api.utils.StringUtils; 22 | import io.ballerina.runtime.api.values.BString; 23 | 24 | /** 25 | * Constants related to Ballerina JWT stdlib. 26 | */ 27 | public class JwtConstants { 28 | 29 | private JwtConstants() {} 30 | 31 | public static final String JWT_ERROR_TYPE = "Error"; 32 | 33 | public static final String SINGLE_SLASH = "/"; 34 | public static final String DOUBLE_SLASH = "//"; 35 | public static final String SCHEME_SEPARATOR = "://"; 36 | public static final String HTTP_SCHEME = "http"; 37 | public static final String HTTPS_SCHEME = "https"; 38 | 39 | public static final BString HTTP_VERSION = StringUtils.fromString("httpVersion"); 40 | public static final BString SECURE_SOCKET = StringUtils.fromString("secureSocket"); 41 | public static final BString DISABLE = StringUtils.fromString("disable"); 42 | public static final BString CERT = StringUtils.fromString("cert"); 43 | public static final BString KEY = StringUtils.fromString("key"); 44 | public static final BString CERT_FILE = StringUtils.fromString("certFile"); 45 | public static final BString KEY_FILE = StringUtils.fromString("keyFile"); 46 | public static final BString KEY_PASSWORD = StringUtils.fromString("keyPassword"); 47 | public static final BString PATH = StringUtils.fromString("path"); 48 | public static final BString PASSWORD = StringUtils.fromString("password"); 49 | 50 | public static final String TLS = "TLS"; 51 | public static final String PKCS12 = "PKCS12"; 52 | public static final String HTTP_2 = "HTTP_2"; 53 | 54 | public static final String NATIVE_DATA_PUBLIC_KEY_CERTIFICATE = "NATIVE_DATA_PUBLIC_KEY_CERTIFICATE"; 55 | public static final String NATIVE_DATA_PRIVATE_KEY = "NATIVE_DATA_PRIVATE_KEY"; 56 | 57 | public static final String RUNTIME_WARNING_PREFIX = "warning: [ballerina/jwt] "; 58 | public static final String HTTPS_RECOMMENDATION_ERROR = "HTTPS is recommended but using HTTP"; 59 | } 60 | -------------------------------------------------------------------------------- /examples/order-management-service/inventory_service/modules/inventory/inventory.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | type ElectronicItem record {| 18 | readonly string code; 19 | string brand; 20 | string model; 21 | string price; 22 | int qty; 23 | |}; 24 | 25 | type BookItem record {| 26 | readonly string code; 27 | string title; 28 | string authors; 29 | string price; 30 | int qty; 31 | |}; 32 | 33 | table key(code) electronicsTable = table [ 34 | { 35 | "code": "APMBA132021", 36 | "brand": "Apple", 37 | "model": "Mac Book AIR M1 (13-inch 2021)", 38 | "price": "$1249.00", 39 | "qty": 32 40 | }, 41 | { 42 | "code": "SOWH1000XM4", 43 | "brand": "Sony", 44 | "model": "WH-1000XM4", 45 | "price": "$349.99", 46 | "qty": 75 47 | } 48 | ]; 49 | 50 | table key(code) booksTable = table [ 51 | { 52 | "code": "978-1617295959", 53 | "title": "Microservices Security in Action", 54 | "authors": "Prabath Siriwardena and Nuwan Dias", 55 | "price": "$50.99", 56 | "qty": 10 57 | }, 58 | { 59 | "code": "978-1484220498", 60 | "title": "Advanced API Security", 61 | "authors": "Prabath Siriwardena", 62 | "price": "$15.39", 63 | "qty": 10 64 | } 65 | ]; 66 | 67 | public type InventoryItem ElectronicItem|BookItem; 68 | 69 | public map> inventory = { 70 | electronics: electronicsTable, 71 | books: booksTable 72 | }; 73 | 74 | public function increaseQty(string itemCategory, string itemCode, int qty) { 75 | InventoryItem item = filterInventoryItem(itemCategory, itemCode); 76 | item.qty += qty; 77 | } 78 | 79 | public function decreaseQty(string itemCategory, string itemCode, int qty) { 80 | InventoryItem item = filterInventoryItem(itemCategory, itemCode); 81 | item.qty -= qty; 82 | } 83 | 84 | function filterInventoryItem(string itemCategory, string itemCode) returns InventoryItem { 85 | table key(code) inventoryTable = key(code)>inventory[itemCategory]; 86 | return inventoryTable.get(itemCode); 87 | } 88 | -------------------------------------------------------------------------------- /compiler-plugin/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.org) 4 | * 5 | * WSO2 LLC. licenses this file to you under the Apache License, 6 | * Version 2.0 (the "License"); you may not use this file except 7 | * in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | plugins { 21 | id 'java' 22 | id 'checkstyle' 23 | id 'com.github.spotbugs' 24 | } 25 | 26 | description = 'Ballerina - JWT Compiler Plugin' 27 | 28 | 29 | dependencies { 30 | checkstyle project(':checkstyle') 31 | checkstyle "com.puppycrawl.tools:checkstyle:${checkstylePluginVersion}" 32 | 33 | implementation group: 'org.ballerinalang', name: 'ballerina-lang', version: "${ballerinaLangVersion}" 34 | implementation group: 'org.ballerinalang', name: 'ballerina-tools-api', version: "${ballerinaLangVersion}" 35 | implementation group: 'org.ballerinalang', name: 'ballerina-parser', version: "${ballerinaLangVersion}" 36 | implementation group: 'io.ballerina.scan', name: 'scan-command', version: "${balScanVersion}" 37 | implementation project(":jwt-native") 38 | } 39 | 40 | def excludePattern = '**/module-info.java' 41 | tasks.withType(Checkstyle).configureEach { 42 | exclude excludePattern 43 | } 44 | 45 | checkstyle { 46 | toolVersion "${project.checkstylePluginVersion}" 47 | configFile rootProject.file("build-config/checkstyle/build/checkstyle.xml") 48 | configProperties = ["suppressionFile" : file("${rootDir}/build-config/checkstyle/build/suppressions.xml")] 49 | } 50 | 51 | checkstyleMain.dependsOn(":checkstyle:downloadCheckstyleRuleFiles") 52 | 53 | spotbugsMain { 54 | def classLoader = plugins["com.github.spotbugs"].class.classLoader 55 | def SpotBugsConfidence = classLoader.findLoadedClass("com.github.spotbugs.snom.Confidence") 56 | def SpotBugsEffort = classLoader.findLoadedClass("com.github.spotbugs.snom.Effort") 57 | effort = SpotBugsEffort.MAX 58 | reportLevel = SpotBugsConfidence.LOW 59 | reportsDir = file("$project.buildDir/reports/spotbugs") 60 | reports { 61 | html.enabled true 62 | text.enabled = true 63 | } 64 | def excludeFile = file("${rootDir}/spotbugs-exclude.xml") 65 | if(excludeFile.exists()) { 66 | excludeFilter = excludeFile 67 | } 68 | } 69 | 70 | compileJava { 71 | doFirst { 72 | options.compilerArgs = [ 73 | '--module-path', classpath.asPath, 74 | ] 75 | classpath = files() 76 | } 77 | } 78 | 79 | build.dependsOn ":jwt-native:build" 80 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | This file contains all the notable changes done to the Ballerina JWT package through the releases. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 5 | 6 | ## [2.15.1] - 2025-10-03 7 | 8 | ### Added 9 | 10 | - [Add support for scan rule to detect 'NONE' algorithm type](https://github.com/ballerina-platform/ballerina-library/issues/7918) 11 | - [Add support to directly provide `crypto:PrivateKey` and `crypto:PublicKey` in JWT signature configurations](https://github.com/ballerina-platform/ballerina-library/issues/6514) 12 | 13 | ## [2.12.1] - 2024-06-14 14 | 15 | ## Changed 16 | - [Revert support to directly provide `crypto:PrivateKey` and `crypto:PublicKey` in JWT signature configurations](https://github.com/ballerina-platform/ballerina-library/issues/6628) 17 | 18 | ## [2.12.0] - 2024-05-31 19 | 20 | ### Added 21 | - [Add support to directly provide `crypto:PrivateKey` and `crypto:PublicKey` in JWT signature configurations](https://github.com/ballerina-platform/ballerina-library/issues/6514) 22 | 23 | ## [2.5.0] - 2022-11-29 24 | 25 | ### Changed 26 | - [API Docs Updated](https://github.com/ballerina-platform/ballerina-standard-library/issues/3463) 27 | 28 | ## [2.3.0] - 2022-05-30 29 | 30 | ### Changed 31 | - [Append the scheme of the HTTP client URL (JWKs) based on the client configurations](https://github.com/ballerina-platform/ballerina-standard-library/issues/2816) 32 | 33 | ## [2.0.0] - 2021-10-10 34 | 35 | ### Added 36 | - [Add HMAC signature support for JWT](https://github.com/ballerina-platform/ballerina-standard-library/issues/1645) 37 | 38 | ## [1.1.0-beta2] - 2021-07-06 39 | 40 | ### Added 41 | - [Improve JWT validation for all the fields of JWT issuer configuration](https://github.com/ballerina-platform/ballerina-standard-library/issues/1240) 42 | 43 | ## [1.1.0-alpha8] - 2021-04-22 44 | 45 | ### Changed 46 | - [Improve error messages and log messages](https://github.com/ballerina-platform/ballerina-standard-library/issues/1242) 47 | 48 | ## [1.1.0-alpha6] - 2021-04-02 49 | 50 | ### Changed 51 | - Remove usages of `checkpanic` for type narrowing 52 | 53 | ## [1.1.0-alpha5] - 2021-03-19 54 | 55 | ### Added 56 | - [Add cert file and mTLS support for JDK11 client](https://github.com/ballerina-platform/ballerina-standard-library/issues/936) 57 | - [Add jti claim as a user input](https://github.com/ballerina-platform/ballerina-standard-library/issues/1210) 58 | 59 | ### Changed 60 | - [Replace base64 URL encode/decode APIs](https://github.com/ballerina-platform/ballerina-standard-library/issues/1212) 61 | - Update error types and log API 62 | - Update for Time API changes 63 | 64 | ### Fixed 65 | - Fix nbf/exp claim validation 66 | 67 | ## [1.1.0-alpha4] - 2021-02-20 68 | 69 | ### Changed 70 | - [Refactor JWT validating API](https://github.com/ballerina-platform/ballerina-standard-library/issues/1213) 71 | - Refactor JWT issue/validate test cases 72 | - Update for crypto API changes 73 | - [Extend private key/public cert support for JWT signature generation/validation](https://github.com/ballerina-platform/ballerina-standard-library/issues/822) 74 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /docs/proposals/enable-crypto-key-usage.md: -------------------------------------------------------------------------------- 1 | # Proposal: Enable direct use of `crypto:PrivateKey` and `crypto:PublicKey` in JWT signature configurations 2 | 3 | _Authors_: @ayeshLK \ 4 | _Reviewers_: @shafreenAnfar @daneshk @NipunaRanasinghe @Bhashinee \ 5 | _Created_: 2024/05/08 \ 6 | _Updated_: 2024/05/08 \ 7 | _Issue_: [#6515](https://github.com/ballerina-platform/ballerina-library/issues/6515) 8 | 9 | ## Summary 10 | 11 | JWT signature configurations are designed to facilitate the generation and verification of JWT signatures. 12 | Therefore, the JWT package should support direct usage of `crypto:PrivateKey` and `crypto:PublicKey` in 13 | `jwt:IssuerSignatureConfig` and `jwt:ValidatorSignatureConfig` respectively. 14 | ## Goals 15 | 16 | - Enable direct use of `crypto:PrivateKey` and `crypto:PublicKey` in JWT signature configurations 17 | 18 | ## Motivation 19 | 20 | JWT signature configurations are required configurations to generate the signature portion of a JWT. Typically, 21 | these configurations involve a private key and a public certificate. In Ballerina, these elements are represented as 22 | `crypto:PrivateKey` and `crypto:PublicKey`, respectively. Therefore, JWT signature configurations should allow the 23 | direct usage of `crypto:PrivateKey` and `crypto:PublicKey` within its API. 24 | 25 | ## Description 26 | 27 | As mentioned in the Goals section the purpose of this proposal is to enable direct use of `crypto:PrivateKey` 28 | and `crypto:PublicKey` in JWT signature configurations. 29 | 30 | The key functionalities expected from this change are as follows, 31 | 32 | - Allow `crypto:PrivateKey` and `crypto:PublicKey` in `jwt:IssuerSignatureConfig` and `jwt:ValidatorSignatureConfig` respectively. 33 | 34 | ### API changes 35 | 36 | Add support for `crypto:PrivateKey` in the `config` field of `jwt:IssuerSignatureConfig` record. 37 | 38 | ```ballerina 39 | # Represents JWT signature configurations. 40 | # 41 | # + algorithm - Cryptographic signing algorithm for JWS 42 | # + config - KeyStore configurations, private key configurations or shared key configurations 43 | public type IssuerSignatureConfig record {| 44 | SigningAlgorithm algorithm = RS256; 45 | record {| 46 | crypto:KeyStore keyStore; 47 | string keyAlias; 48 | string keyPassword; 49 | |} | record {| 50 | string keyFile; 51 | string keyPassword?; 52 | |}|crypto:PrivateKey|string config?; 53 | |}; 54 | ``` 55 | 56 | Add support for `crypto:PublicKey` in the `certFile` field of `jwt:ValidatorSignatureConfig` record. 57 | 58 | ```ballerina 59 | # Represents JWT signature configurations. 60 | # 61 | # + jwksConfig - JWKS configurations 62 | # + certFile - Public certificate file 63 | # + trustStoreConfig - JWT TrustStore configurations 64 | # + secret - HMAC secret configuration 65 | public type ValidatorSignatureConfig record {| 66 | record {| 67 | string url; 68 | cache:CacheConfig cacheConfig?; 69 | ClientConfiguration clientConfig = {}; 70 | |} jwksConfig?; 71 | string|crypto:PublicKey certFile?; 72 | record {| 73 | crypto:TrustStore trustStore; 74 | string certAlias; 75 | |} trustStoreConfig?; 76 | string secret?; 77 | |}; 78 | ``` 79 | 80 | ## Dependencies 81 | 82 | - [#6513](https://github.com/ballerina-platform/ballerina-library/issues/6513) 83 | -------------------------------------------------------------------------------- /ballerina/jwt_commons.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/jballerina.java; 18 | 19 | # Represents the cryptographic algorithms used to secure the JWS. 20 | public type SigningAlgorithm RS256|RS384|RS512|HS256|HS384|HS512|NONE; 21 | 22 | # The `RSA-SHA256` algorithm. 23 | public const RS256 = "RS256"; 24 | 25 | # The `RSA-SHA384` algorithm. 26 | public const RS384 = "RS384"; 27 | 28 | # The `RSA-SHA512` algorithm. 29 | public const RS512 = "RS512"; 30 | 31 | # The `HMAC-SHA256` algorithm. 32 | public const HS256 = "HS256"; 33 | 34 | # The `HMAC-SHA384` algorithm. 35 | public const HS384 = "HS384"; 36 | 37 | # The `HMAC-SHA512` algorithm. 38 | public const HS512 = "HS512"; 39 | 40 | # Unsecured JWS (no signing). 41 | public const NONE = "none"; 42 | 43 | //JOSH header parameters 44 | const string ALG = "alg"; 45 | const string TYP = "typ"; 46 | const string CTY = "cty"; 47 | const string KID = "kid"; 48 | 49 | //Payload parameters 50 | const string ISS = "iss"; 51 | const string SUB = "sub"; 52 | const string AUD = "aud"; 53 | const string JTI = "jti"; 54 | const string EXP = "exp"; 55 | const string NBF = "nbf"; 56 | const string IAT = "iat"; 57 | 58 | # Represents JWT header. 59 | # 60 | # + alg - Cryptographic algorithm used to secure the JWS. Supported values: `RS256`, `RS384`, `RS512`, `HS256`, `HS384`, `HS512`, `NONE`. 61 | # + typ - Media type of the JWT 62 | # + cty - Content type, conveys structural information about the JWT 63 | # + kid - Key ID, a hint indicating which key was used to secure the JWS 64 | public type Header record { 65 | SigningAlgorithm alg?; 66 | string typ?; 67 | string cty?; 68 | string kid?; 69 | }; 70 | 71 | # Represents JWT payload. 72 | # 73 | # + iss - Issuer, identifies the principal that issued the JWT 74 | # + sub - Subject, identifies the principal that is the subject of the JWT 75 | # + aud - Audience, identifies the recipients that the JWT is intended for 76 | # + jti - JWT ID, unique identifier for the JWT 77 | # + exp - Expiration time, identifies the expiration time (in seconds since the Epoch) on or after which the JWT must not be accepted 78 | # + nbf - Not before, identifies the time (in seconds since the Epoch) before which the JWT must not be accepted 79 | # + iat - Issued at, identifies the time (in seconds since the Epoch) at which the JWT was issued 80 | public type Payload record { 81 | string iss?; 82 | string sub?; 83 | string|string[] aud?; 84 | int exp?; 85 | int nbf?; 86 | int iat?; 87 | string jti?; 88 | }; 89 | 90 | isolated function encodeBase64Url(byte[] input) returns string = @java:Method { 91 | 'class: "io.ballerina.stdlib.jwt.JwtUtils" 92 | } external; 93 | 94 | isolated function decodeBase64Url(string input) returns byte[]|Error = @java:Method { 95 | 'class: "io.ballerina.stdlib.jwt.JwtUtils" 96 | } external; 97 | -------------------------------------------------------------------------------- /compiler-plugin-tests/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.org) 3 | * 4 | * WSO2 LLC. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | plugins { 20 | id 'java' 21 | id 'checkstyle' 22 | id 'com.github.spotbugs' 23 | } 24 | 25 | description = 'Ballerina - JWT Compiler Plugin Tests' 26 | 27 | dependencies { 28 | checkstyle project(':checkstyle') 29 | checkstyle "com.puppycrawl.tools:checkstyle:${checkstylePluginVersion}" 30 | 31 | implementation project(':jwt-compiler-plugin') 32 | 33 | testImplementation group: 'org.ballerinalang', name: 'ballerina-lang', version: "${ballerinaLangVersion}" 34 | testImplementation group: 'org.ballerinalang', name: 'ballerina-tools-api', version: "${ballerinaLangVersion}" 35 | testImplementation group: 'org.ballerinalang', name: 'ballerina-parser', version: "${ballerinaLangVersion}" 36 | testImplementation group: 'io.ballerina.scan', name: 'scan-command', version: "${balScanVersion}" 37 | testImplementation group: 'io.ballerina.scan', name: 'scan-command-test-utils', version: "${balScanVersion}" 38 | testImplementation group: 'org.testng', name: 'testng', version: "${testngVersion}" 39 | testImplementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: "${jacksonDatabindVersion}" 40 | } 41 | tasks.withType(JavaCompile).configureEach { 42 | options.encoding = 'UTF-8' 43 | } 44 | 45 | sourceCompatibility = JavaVersion.VERSION_21 46 | 47 | test { 48 | systemProperty "ballerina.offline.flag", "true" 49 | useTestNG() { 50 | suites 'src/test/resources/testng.xml' 51 | } 52 | testLogging.showStandardStreams = true 53 | testLogging { 54 | events "PASSED", "FAILED", "SKIPPED" 55 | afterSuite { desc, result -> 56 | if (!desc.parent) { // will match the outermost suite 57 | def output = "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)" 58 | def startItem = '| ', endItem = ' |' 59 | def repeatLength = startItem.length() + output.length() + endItem.length() 60 | println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + ('-' * repeatLength)) 61 | } 62 | } 63 | } 64 | } 65 | 66 | spotbugsTest { 67 | def classLoader = plugins["com.github.spotbugs"].class.classLoader 68 | def SpotBugsConfidence = classLoader.findLoadedClass("com.github.spotbugs.snom.Confidence") 69 | def SpotBugsEffort = classLoader.findLoadedClass("com.github.spotbugs.snom.Effort") 70 | ignoreFailures = true 71 | effort = SpotBugsEffort.MAX 72 | reportLevel = SpotBugsConfidence.LOW 73 | reportsDir = file("$project.buildDir/reports/spotbugs") 74 | def excludeFile = file("${rootDir}/build-config/spotbugs-exclude.xml") 75 | if (excludeFile.exists()) { 76 | it.excludeFilter = excludeFile 77 | } 78 | reports { 79 | text.enabled = true 80 | } 81 | } 82 | 83 | spotbugsMain { 84 | enabled false 85 | } 86 | 87 | tasks.register('validateSpotbugs') { 88 | doLast { 89 | if (spotbugsMain.reports.size() > 0 && 90 | spotbugsMain.reports[0].destination.exists() && 91 | spotbugsMain.reports[0].destination.text.readLines().size() > 0) { 92 | spotbugsMain.reports[0].destination?.eachLine { 93 | println 'Failure: ' + it 94 | } 95 | throw new GradleException("Spotbugs rule violations were found."); 96 | } 97 | } 98 | } 99 | 100 | tasks.withType(Checkstyle).configureEach { 101 | exclude '**/module-info.java' 102 | } 103 | 104 | checkstyle { 105 | toolVersion "${project.checkstylePluginVersion}" 106 | configFile rootProject.file("build-config/checkstyle/build/checkstyle.xml") 107 | configProperties = ["suppressionFile": file("${rootDir}/build-config/checkstyle/build/suppressions.xml")] 108 | } 109 | 110 | checkstyleMain { 111 | enabled false 112 | } 113 | 114 | spotbugsTest.finalizedBy validateSpotbugs 115 | checkstyleTest.dependsOn ':checkstyle:downloadCheckstyleRuleFiles' 116 | 117 | compileJava { 118 | doFirst { 119 | options.compilerArgs = [ 120 | '--module-path', classpath.asPath, 121 | ] 122 | classpath = files() 123 | } 124 | } 125 | 126 | test.dependsOn ":jwt-ballerina:build" 127 | build.dependsOn ":jwt-ballerina:build" 128 | -------------------------------------------------------------------------------- /ballerina/tests/listener_jwt_auth_provider_test.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | // NOTE: All the tokens/credentials used in this test are dummy tokens/credentials and used only for testing purposes. 18 | 19 | import ballerina/cache; 20 | import ballerina/test; 21 | 22 | @test:Config {} 23 | isolated function testListenerJwtAuthProviderSuccess() returns Error? { 24 | ValidatorConfig jwtConfig = { 25 | issuer: "wso2", 26 | audience: "ballerina", 27 | cacheConfig: { 28 | capacity: 10, 29 | evictionFactor: 0.25, 30 | evictionPolicy: cache:LRU, 31 | defaultMaxAge: -1, 32 | cleanupInterval: 3600 33 | }, 34 | signatureConfig: { 35 | trustStoreConfig: { 36 | trustStore: { 37 | path: TRUSTSTORE_PATH, 38 | password: "ballerina" 39 | }, 40 | certAlias: "ballerina" 41 | } 42 | } 43 | }; 44 | ListenerJwtAuthProvider jwtAuthProvider = new(jwtConfig); 45 | Payload result = check jwtAuthProvider.authenticate(JWT1); 46 | test:assertEquals(result?.iss, "wso2"); 47 | test:assertEquals(result?.aud, ["ballerina","ballerinaSamples"]); 48 | // Authenticate the token from the cache 49 | result = check jwtAuthProvider.authenticate(JWT1); 50 | test:assertEquals(result?.iss, "wso2"); 51 | test:assertEquals(result?.aud, ["ballerina","ballerinaSamples"]); 52 | } 53 | 54 | @test:Config { 55 | groups: ["jwks"] 56 | } 57 | isolated function testListenerJwtAuthProviderSuccessWithJwk() returns Error? { 58 | ValidatorConfig jwtConfig = { 59 | issuer: "ballerina", 60 | audience: "vEwzbcasJVQm1jVYHUHCjhxZ4tYa", 61 | cacheConfig: { 62 | capacity: 10, 63 | evictionFactor: 0.25, 64 | evictionPolicy: cache:LRU, 65 | defaultMaxAge: -1, 66 | cleanupInterval: 3600 67 | }, 68 | signatureConfig: { 69 | jwksConfig: { 70 | url: "https://localhost:9445/oauth2/jwks", 71 | cacheConfig: { 72 | capacity: 10, 73 | evictionFactor: 0.25, 74 | evictionPolicy: cache:LRU, 75 | defaultMaxAge: -1, 76 | cleanupInterval: 3600 77 | }, 78 | clientConfig: { 79 | secureSocket: { 80 | cert: { 81 | path: TRUSTSTORE_PATH, 82 | password: "ballerina" 83 | } 84 | } 85 | } 86 | } 87 | } 88 | }; 89 | ListenerJwtAuthProvider jwtAuthProvider = new(jwtConfig); 90 | Payload result = check jwtAuthProvider.authenticate(JWT2); 91 | test:assertEquals(result?.iss, "ballerina"); 92 | test:assertEquals(result?.aud, ["vEwzbcasJVQm1jVYHUHCjhxZ4tYa"]); 93 | } 94 | 95 | @test:Config {} 96 | isolated function testListenerJwtAuthProviderFailure() { 97 | ValidatorConfig jwtConfig = { 98 | issuer: "invalid", 99 | audience: "ballerina", 100 | signatureConfig: { 101 | trustStoreConfig: { 102 | trustStore: { 103 | path: TRUSTSTORE_PATH, 104 | password: "ballerina" 105 | }, 106 | certAlias: "ballerina" 107 | } 108 | } 109 | }; 110 | ListenerJwtAuthProvider jwtAuthProvider = new(jwtConfig); 111 | Payload|Error result = jwtAuthProvider.authenticate(JWT1); 112 | if result is Error { 113 | test:assertEquals(result.message(), "JWT validation failed."); 114 | } else { 115 | test:assertFail("Expected error not found."); 116 | } 117 | } 118 | 119 | @test:Config {} 120 | isolated function testListenerJwtAuthProviderFailureWithInvalidCredential() { 121 | ValidatorConfig jwtConfig = { 122 | issuer: "wso2", 123 | audience: "ballerina", 124 | signatureConfig: { 125 | trustStoreConfig: { 126 | trustStore: { 127 | path: TRUSTSTORE_PATH, 128 | password: "ballerina" 129 | }, 130 | certAlias: "ballerina" 131 | } 132 | } 133 | }; 134 | ListenerJwtAuthProvider jwtAuthProvider = new(jwtConfig); 135 | Payload|Error result = jwtAuthProvider.authenticate("invalid_credential"); 136 | if result is Error { 137 | test:assertEquals(result.message(), "Credential format does not match to JWT format."); 138 | } else { 139 | test:assertFail("Expected error not found."); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /ballerina/Dependencies.toml: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED FILE. DO NOT MODIFY. 2 | 3 | # This file is auto-generated by Ballerina for managing dependency versions. 4 | # It should not be modified by hand. 5 | 6 | [ballerina] 7 | dependencies-toml-version = "2" 8 | distribution-version = "2201.12.0" 9 | 10 | [[package]] 11 | org = "ballerina" 12 | name = "cache" 13 | version = "3.10.0" 14 | dependencies = [ 15 | {org = "ballerina", name = "constraint"}, 16 | {org = "ballerina", name = "jballerina.java"}, 17 | {org = "ballerina", name = "task"}, 18 | {org = "ballerina", name = "time"} 19 | ] 20 | modules = [ 21 | {org = "ballerina", packageName = "cache", moduleName = "cache"} 22 | ] 23 | 24 | [[package]] 25 | org = "ballerina" 26 | name = "constraint" 27 | version = "1.7.0" 28 | dependencies = [ 29 | {org = "ballerina", name = "jballerina.java"} 30 | ] 31 | 32 | [[package]] 33 | org = "ballerina" 34 | name = "crypto" 35 | version = "2.9.0" 36 | dependencies = [ 37 | {org = "ballerina", name = "jballerina.java"}, 38 | {org = "ballerina", name = "time"} 39 | ] 40 | modules = [ 41 | {org = "ballerina", packageName = "crypto", moduleName = "crypto"} 42 | ] 43 | 44 | [[package]] 45 | org = "ballerina" 46 | name = "io" 47 | version = "1.8.0" 48 | dependencies = [ 49 | {org = "ballerina", name = "jballerina.java"}, 50 | {org = "ballerina", name = "lang.value"} 51 | ] 52 | modules = [ 53 | {org = "ballerina", packageName = "io", moduleName = "io"} 54 | ] 55 | 56 | [[package]] 57 | org = "ballerina" 58 | name = "jballerina.java" 59 | version = "0.0.0" 60 | modules = [ 61 | {org = "ballerina", packageName = "jballerina.java", moduleName = "jballerina.java"} 62 | ] 63 | 64 | [[package]] 65 | org = "ballerina" 66 | name = "jwt" 67 | version = "2.15.1" 68 | dependencies = [ 69 | {org = "ballerina", name = "cache"}, 70 | {org = "ballerina", name = "crypto"}, 71 | {org = "ballerina", name = "io"}, 72 | {org = "ballerina", name = "jballerina.java"}, 73 | {org = "ballerina", name = "lang.int"}, 74 | {org = "ballerina", name = "lang.string"}, 75 | {org = "ballerina", name = "log"}, 76 | {org = "ballerina", name = "test"}, 77 | {org = "ballerina", name = "time"} 78 | ] 79 | modules = [ 80 | {org = "ballerina", packageName = "jwt", moduleName = "jwt"} 81 | ] 82 | 83 | [[package]] 84 | org = "ballerina" 85 | name = "lang.__internal" 86 | version = "0.0.0" 87 | dependencies = [ 88 | {org = "ballerina", name = "jballerina.java"}, 89 | {org = "ballerina", name = "lang.object"} 90 | ] 91 | 92 | [[package]] 93 | org = "ballerina" 94 | name = "lang.array" 95 | version = "0.0.0" 96 | scope = "testOnly" 97 | dependencies = [ 98 | {org = "ballerina", name = "jballerina.java"}, 99 | {org = "ballerina", name = "lang.__internal"} 100 | ] 101 | 102 | [[package]] 103 | org = "ballerina" 104 | name = "lang.error" 105 | version = "0.0.0" 106 | scope = "testOnly" 107 | dependencies = [ 108 | {org = "ballerina", name = "jballerina.java"} 109 | ] 110 | 111 | [[package]] 112 | org = "ballerina" 113 | name = "lang.int" 114 | version = "0.0.0" 115 | dependencies = [ 116 | {org = "ballerina", name = "jballerina.java"}, 117 | {org = "ballerina", name = "lang.__internal"}, 118 | {org = "ballerina", name = "lang.object"} 119 | ] 120 | modules = [ 121 | {org = "ballerina", packageName = "lang.int", moduleName = "lang.int"} 122 | ] 123 | 124 | [[package]] 125 | org = "ballerina" 126 | name = "lang.object" 127 | version = "0.0.0" 128 | 129 | [[package]] 130 | org = "ballerina" 131 | name = "lang.regexp" 132 | version = "0.0.0" 133 | dependencies = [ 134 | {org = "ballerina", name = "jballerina.java"} 135 | ] 136 | 137 | [[package]] 138 | org = "ballerina" 139 | name = "lang.string" 140 | version = "0.0.0" 141 | dependencies = [ 142 | {org = "ballerina", name = "jballerina.java"}, 143 | {org = "ballerina", name = "lang.regexp"} 144 | ] 145 | modules = [ 146 | {org = "ballerina", packageName = "lang.string", moduleName = "lang.string"} 147 | ] 148 | 149 | [[package]] 150 | org = "ballerina" 151 | name = "lang.value" 152 | version = "0.0.0" 153 | dependencies = [ 154 | {org = "ballerina", name = "jballerina.java"} 155 | ] 156 | 157 | [[package]] 158 | org = "ballerina" 159 | name = "log" 160 | version = "2.12.0" 161 | dependencies = [ 162 | {org = "ballerina", name = "io"}, 163 | {org = "ballerina", name = "jballerina.java"}, 164 | {org = "ballerina", name = "lang.value"}, 165 | {org = "ballerina", name = "observe"} 166 | ] 167 | modules = [ 168 | {org = "ballerina", packageName = "log", moduleName = "log"} 169 | ] 170 | 171 | [[package]] 172 | org = "ballerina" 173 | name = "observe" 174 | version = "1.5.0" 175 | dependencies = [ 176 | {org = "ballerina", name = "jballerina.java"} 177 | ] 178 | 179 | [[package]] 180 | org = "ballerina" 181 | name = "task" 182 | version = "2.7.0" 183 | dependencies = [ 184 | {org = "ballerina", name = "jballerina.java"}, 185 | {org = "ballerina", name = "time"} 186 | ] 187 | 188 | [[package]] 189 | org = "ballerina" 190 | name = "test" 191 | version = "0.0.0" 192 | scope = "testOnly" 193 | dependencies = [ 194 | {org = "ballerina", name = "jballerina.java"}, 195 | {org = "ballerina", name = "lang.array"}, 196 | {org = "ballerina", name = "lang.error"} 197 | ] 198 | modules = [ 199 | {org = "ballerina", packageName = "test", moduleName = "test"} 200 | ] 201 | 202 | [[package]] 203 | org = "ballerina" 204 | name = "time" 205 | version = "2.7.0" 206 | dependencies = [ 207 | {org = "ballerina", name = "jballerina.java"} 208 | ] 209 | modules = [ 210 | {org = "ballerina", packageName = "time", moduleName = "time"} 211 | ] 212 | 213 | -------------------------------------------------------------------------------- /examples/order-management-service/order_service/order_service.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/cache; 18 | import ballerina/http; 19 | import order_service.representations as rep; 20 | 21 | // In memory storage used to store the orders 22 | map ordersMap = {}; 23 | 24 | http:Client inventoryClient = check new ("https://localhost:9091", 25 | secureSocket = { 26 | cert: "./resources/public.crt", 27 | key: { 28 | certFile: "./resources/public.crt", 29 | keyFile: "./resources/private.key" 30 | } 31 | } 32 | ); 33 | 34 | http:JwtValidatorConfig config = { 35 | issuer: "wso2", 36 | audience: "ballerina", 37 | signatureConfig: { 38 | jwksConfig: { 39 | url: "https://localhost:9445/oauth2/jwks", 40 | clientConfig: { 41 | secureSocket: { 42 | cert: "./resources/public.crt" 43 | } 44 | }, 45 | cacheConfig: { 46 | capacity: 10, 47 | evictionFactor: 0.25, 48 | evictionPolicy: cache:LRU, 49 | defaultMaxAge: -1 50 | } 51 | } 52 | }, 53 | cacheConfig: { 54 | capacity: 10, 55 | evictionFactor: 0.25, 56 | evictionPolicy: cache:LRU, 57 | defaultMaxAge: -1 58 | } 59 | }; 60 | 61 | listener http:Listener orderEP = new (9090, 62 | secureSocket = { 63 | key: { 64 | certFile: "./resources/public.crt", 65 | keyFile: "./resources/private.key" 66 | } 67 | } 68 | ); 69 | 70 | service /'order on orderEP { 71 | 72 | @http:ResourceConfig { 73 | auth: [ 74 | { 75 | jwtValidatorConfig: config, 76 | scopes: "add_order" 77 | } 78 | ] 79 | } 80 | resource function post .(@http:Payload rep:Order 'order) returns rep:OrderCreated|error { 81 | string orderId = 'order.id; 82 | ordersMap[orderId] = 'order; 83 | check updateInventoryQty('order.items, rep:DECREASE); 84 | return { 85 | body: {status: "Order '" + orderId + "' created."}, 86 | headers: {"Location": "http://localhost:9090/order/" + orderId} 87 | }; 88 | } 89 | 90 | @http:ResourceConfig { 91 | auth: [ 92 | { 93 | jwtValidatorConfig: config, 94 | scopes: "update_order" 95 | } 96 | ] 97 | } 98 | resource function put [string orderId](@http:Payload rep:UpdateOrder updateOrder) 99 | returns rep:OrderUpdated|rep:OrderNotFound|error { 100 | rep:Order? existingOrder = ordersMap[orderId]; 101 | if existingOrder is rep:Order { 102 | check updateInventoryQty(existingOrder.items, rep:INCREASE); 103 | existingOrder.name = updateOrder.name; 104 | existingOrder.items = updateOrder.items; 105 | ordersMap[orderId] = existingOrder; 106 | check updateInventoryQty(existingOrder.items, rep:DECREASE); 107 | return { 108 | body: {status: "Order '" + orderId + "' updated."} 109 | }; 110 | } 111 | return { 112 | body: {status: "Order '" + orderId + "' cannot be found."} 113 | }; 114 | } 115 | 116 | @http:ResourceConfig { 117 | auth: [ 118 | { 119 | jwtValidatorConfig: config, 120 | scopes: "cancel_order" 121 | } 122 | ] 123 | } 124 | resource function delete [string orderId]() returns rep:OrderCanceled|rep:OrderNotFound|error { 125 | if ordersMap.hasKey(orderId) { 126 | rep:Order 'order = ordersMap.remove(orderId); 127 | check updateInventoryQty('order.items, rep:INCREASE); 128 | return { 129 | body: {status: "Order '" + orderId + "' removed."} 130 | }; 131 | } 132 | return { 133 | body: {status: "Order '" + orderId + "' cannot be found."} 134 | }; 135 | } 136 | 137 | resource function get [string orderId]() returns rep:Order|http:NotFound { 138 | if ordersMap.hasKey(orderId) { 139 | return ordersMap[orderId]; 140 | } 141 | return {body: {status: "Order '" + orderId + "' cannot be found."}}; 142 | } 143 | } 144 | 145 | function updateInventoryQty(rep:OrderItem[] items, rep:InventoryOperation operation) returns error? { 146 | json|http:ClientError response = inventoryClient->put("/inventory/" + operation, items); 147 | if response is http:ClientError { 148 | return error("Failed to " + operation + " the inventory quantity.", response); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /load-tests/order_management_service/scripts/http-post-request.jmx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | false 7 | true 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | continue 17 | 18 | false 19 | -1 20 | 21 | ${__P(users)} 22 | ${__P(rampUpPeriod,60)} 23 | true 24 | ${__P(duration)} 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | ${__P(payload)} 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | ${__P(host,localhost)} 42 | ${__P(port,9090)} 43 | ${__P(protocol,http)} 44 | 45 | ${__P(path)} 46 | POST 47 | true 48 | false 49 | true 50 | false 51 | 52 | HttpClient4 53 | 10000 54 | 120000 55 | 56 | 57 | 58 | 59 | 60 | Content-Type 61 | application/json 62 | 63 | 64 | Authorization 65 | Bearer eyJhbGciOiJSUzI1NiIsICJ0eXAiOiJKV1QiLCAia2lkIjoiTlRBeFptTXhORE15WkRnM01UVTFaR00wTXpFek9ESmhaV0k0TkRObFpEVTFPR0ZrTmpGaU1RIn0.eyJpc3MiOiJ3c28yIiwgInN1YiI6ImNvdW50ZXIiLCAiYXVkIjoiYmFsbGVyaW5hIiwgImV4cCI6MTk0NTIzODg5NCwgIm5iZiI6MTYyOTg3ODg5NCwgImlhdCI6MTYyOTg3ODg5NCwgImp0aSI6ImY1YWRlZDUwNTg1YzQ2ZjJiOGNhMjMzZDBjMmEzYzlkIiwgInNjb3BlIjoiYWRkX29yZGVyIn0.FMDL-Y8zMtAW7vBVeEbwf_8ynSKdjAkuEBVJpoGds06Z2cOrL3WPZYswnbUOzULBnOXQJQEnK-P-QmypvqP0NWGpzfoC5QMqg0FhPvxyglP1QNYbrS_5tZFM_7Nn7tBFZxiBq666AzjDyhZ2jc39X9rftV0m8p4yyFdSI4KFiEzQ8eOWVVOIg3ejQ0ruDlSVvwx3lXe03XYmrhQp0m-_KYm-SGV3HTZttbo00A250pVY1QL137WDgeeDtGchOfjvM9G-UL8t3sw1Lyoq4OzblHEJJQOJnfRLM_nFKeULuAtt5k_UwmQRlV-XrgxMk0lu6KvYt1-Sa1HMiS8aIvrwig 66 | 67 | 68 | 69 | 70 | 71 | 72 | ${__P(response_size)} 73 | 74 | 75 | Assertion.response_data 76 | false 77 | 16 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /ballerina/listener_jwt_auth_provider.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/cache; 18 | import ballerina/log; 19 | import ballerina/time; 20 | 21 | # Represents the listener JWT Auth provider, which is used to authenticate the provided credentials (JWT) against 22 | # the provided JWT validator configurations. 23 | # ```ballerina 24 | # jwt:ListenerJwtAuthProvider provider = new({ 25 | # issuer: "example", 26 | # audience: "ballerina", 27 | # signatureConfig: { 28 | # certificateAlias: "ballerina", 29 | # trustStore: { 30 | # path: "/path/to/truststore.p12", 31 | # password: "ballerina" 32 | # } 33 | # } 34 | # }); 35 | # ``` 36 | public isolated class ListenerJwtAuthProvider { 37 | 38 | private final ValidatorConfig & readonly validatorConfig; 39 | private final cache:Cache? jwtCache; 40 | private final cache:Cache? jwksCache; 41 | 42 | # Provides authentication based on the provided JWT. 43 | # 44 | # + validatorConfig - JWT validator configurations 45 | public isolated function init(ValidatorConfig validatorConfig) { 46 | self.validatorConfig = validatorConfig.cloneReadOnly(); 47 | cache:CacheConfig? jwtCacheConfig = self.validatorConfig?.cacheConfig; 48 | if jwtCacheConfig is cache:CacheConfig { 49 | self.jwtCache = new(jwtCacheConfig); 50 | } else { 51 | self.jwtCache = (); 52 | } 53 | var jwksConfig = self.validatorConfig?.signatureConfig?.jwksConfig; 54 | if jwksConfig !is () { 55 | ClientConfiguration clientConfig = jwksConfig.clientConfig; 56 | cache:CacheConfig? jwksCacheConfig = jwksConfig?.cacheConfig; 57 | if jwksCacheConfig is cache:CacheConfig { 58 | self.jwksCache = new(jwksCacheConfig); 59 | Error? result = preloadJwksToCache( (self.jwksCache), jwksConfig.url, clientConfig); 60 | if result is Error { 61 | panic result; 62 | } 63 | return; 64 | } 65 | } 66 | self.jwksCache = (); 67 | } 68 | 69 | # Authenticates the provided JWT against the configured validator. 70 | #```ballerina 71 | # boolean result = check provider.authenticate(""); 72 | # ``` 73 | # 74 | # + credential - JWT to be authenticated 75 | # + return - `jwt:Payload` if authentication is successful or else a `jwt:Error` if an error occurred 76 | public isolated function authenticate(string credential) returns Payload|Error { 77 | string[] jwtComponents = re `\.`.split(credential); 78 | if jwtComponents.length() != 3 { 79 | return prepareError("Credential format does not match to JWT format."); 80 | } 81 | 82 | cache:Cache? jwtCache = self.jwtCache; 83 | if jwtCache is cache:Cache && jwtCache.hasKey(credential) { 84 | Payload? payload = validateFromCache(jwtCache, credential); 85 | if payload is Payload { 86 | return payload; 87 | } 88 | } 89 | 90 | Payload|Error validationResult = validateJwt(credential, self.validatorConfig, self.jwksCache); 91 | if validationResult is Payload { 92 | if jwtCache is cache:Cache { 93 | addToCache(jwtCache, credential, validationResult); 94 | } 95 | return validationResult; 96 | } else { 97 | return prepareError("JWT validation failed.", validationResult); 98 | } 99 | } 100 | } 101 | 102 | isolated function preloadJwksToCache(cache:Cache jwksCache, string url, ClientConfiguration clientConfig) returns Error? { 103 | string|Error stringResponse = getJwksResponse(url, clientConfig); 104 | if stringResponse is string { 105 | json[] jwksArray = check getJwksArray(stringResponse); 106 | foreach json jwk in jwksArray { 107 | json|error kid = jwk.kid; 108 | if kid is string { 109 | cache:Error? cachedResult = jwksCache.put(kid, jwk); 110 | if cachedResult is cache:Error { 111 | return prepareError("Failed to put JWK for the kid '" + kid + "' to the cache.", cachedResult); 112 | } 113 | } else if kid is error { 114 | return prepareError("Failed to access 'kid' property from the JSON '" + jwk.toString() + "'.", kid); 115 | } else { 116 | return prepareError("Failed to extract 'kid' property as a 'string' from the JSON '" + jwk.toString() + "'."); 117 | } 118 | } 119 | return; 120 | } else { 121 | return prepareError("Failed to call JWKS endpoint to preload JWKS to the cache.", stringResponse); 122 | } 123 | } 124 | 125 | isolated function validateFromCache(cache:Cache jwtCache, string jwt) returns Payload? { 126 | any|cache:Error cachedResult = jwtCache.get(jwt); 127 | if cachedResult is any { 128 | Payload payload = cachedResult; 129 | int? expTime = payload?.exp; 130 | // convert to current time and check the expiry time 131 | [int, decimal] currentTime = time:utcNow(); 132 | if expTime is () || expTime > currentTime[0] { 133 | return payload; 134 | } 135 | cache:Error? result = jwtCache.invalidate(jwt); 136 | if result is cache:Error { 137 | log:printDebug("Failed to invalidate JWT from the cache.", 'error = result); 138 | } 139 | } else { 140 | log:printDebug("Failed to retrieve JWT entry from the cache.", 'error = cachedResult); 141 | } 142 | return; 143 | } 144 | 145 | isolated function addToCache(cache:Cache jwtCache, string jwt, Payload payload) { 146 | cache:Error? result = jwtCache.put(jwt, payload); 147 | if result is cache:Error { 148 | log:printDebug("Failed to add JWT to the cache.", 'error = result); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /ballerina/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | import org.apache.tools.ant.taskdefs.condition.Os 20 | 21 | plugins { 22 | id 'io.ballerina.plugin' 23 | } 24 | 25 | description = 'Ballerina - JWT Ballerina Generator' 26 | 27 | def packageName = "jwt" 28 | def packageOrg = "ballerina" 29 | def tomlVersion = stripBallerinaExtensionVersion("${project.version}") 30 | def ballerinaTomlFile = new File("$project.projectDir/Ballerina.toml") 31 | def ballerinaTomlFilePlaceHolder = new File("${project.rootDir}/build-config/resources/Ballerina.toml") 32 | def compilerPluginTomlFilePlaceHolder = new File("${project.rootDir}/build-config/resources/CompilerPlugin.toml") 33 | def compilerPluginTomlFile = new File("$project.projectDir/CompilerPlugin.toml") 34 | 35 | def stripBallerinaExtensionVersion(String extVersion) { 36 | if (extVersion.matches(project.ext.timestampedVersionRegex)) { 37 | def splitVersion = extVersion.split('-') 38 | if (splitVersion.length > 3) { 39 | def strippedValues = splitVersion[0..-4] 40 | return strippedValues.join('-') 41 | } else { 42 | return extVersion 43 | } 44 | } else { 45 | return extVersion.replace("${project.ext.snapshotVersion}", "") 46 | } 47 | } 48 | 49 | ballerina { 50 | packageOrganization = packageOrg 51 | module = packageName 52 | langVersion = ballerinaLangVersion 53 | } 54 | 55 | tasks.register('updateTomlFiles') { 56 | doLast { 57 | def newBallerinaToml = ballerinaTomlFilePlaceHolder.text.replace("@project.version@", project.version) 58 | def newCompilerPluginToml = compilerPluginTomlFilePlaceHolder.text.replace("@project.version@", project.version) 59 | newBallerinaToml = newBallerinaToml.replace("@toml.version@", tomlVersion) 60 | ballerinaTomlFile.text = newBallerinaToml 61 | compilerPluginTomlFile.text = newCompilerPluginToml 62 | } 63 | } 64 | 65 | tasks.register('commitTomlFiles') { 66 | doLast { 67 | project.exec { 68 | ignoreExitValue true 69 | if (Os.isFamily(Os.FAMILY_WINDOWS)) { 70 | commandLine 'cmd', '/c', "git commit -m \"[Automated] Update the native jar versions\" Ballerina.toml Dependencies.toml CompilerPlugin.toml" 71 | } else { 72 | commandLine 'sh', '-c', "git commit -m '[Automated] Update the native jar versions' Ballerina.toml Dependencies.toml CompilerPlugin.toml" 73 | } 74 | } 75 | } 76 | } 77 | 78 | publishing { 79 | publications { 80 | maven(MavenPublication) { 81 | artifact source: createArtifactZip, extension: 'zip' 82 | } 83 | } 84 | repositories { 85 | maven { 86 | name = "GitHubPackages" 87 | url = uri("https://maven.pkg.github.com/ballerina-platform/module-${packageOrg}-${packageName}") 88 | credentials { 89 | username = System.getenv("publishUser") 90 | password = System.getenv("publishPAT") 91 | } 92 | } 93 | } 94 | } 95 | 96 | tasks.register('startBallerinaSTS') { 97 | doLast { 98 | // This check is added to prevent starting the server in Windows OS, since the Docker image does not support 99 | // for Windows OS. 100 | if (!Os.isFamily(Os.FAMILY_WINDOWS)) { 101 | def stdOut = new ByteArrayOutputStream() 102 | exec { 103 | commandLine 'sh', '-c', "docker ps --filter name=ballerina-sts" 104 | standardOutput = stdOut 105 | } 106 | if (!stdOut.toString().contains("ballerina-sts")) { 107 | println "Starting Ballerina STS." 108 | exec { 109 | commandLine 'sh', '-c', "docker run --rm -d -p 9445:9445 -p 9444:9444 --name ballerina-sts ldclakmal/ballerina-sts:latest" 110 | standardOutput = stdOut 111 | } 112 | println stdOut.toString() 113 | println "Waiting 10s until the Ballerina STS get initiated." 114 | sleep(15 * 1000) 115 | } else { 116 | println "Ballerina STS is already started." 117 | } 118 | } 119 | } 120 | } 121 | startBallerinaSTS.onlyIf { !project.gradle.startParameter.excludedTaskNames.contains('test') } 122 | 123 | tasks.register('stopBallerinaSTS') { 124 | doLast { 125 | // This check is added to prevent trying to stop the server in Windows OS, since the Docker image not started 126 | // in Windows OS. 127 | if (!Os.isFamily(Os.FAMILY_WINDOWS)) { 128 | def stdOut = new ByteArrayOutputStream() 129 | exec { 130 | commandLine 'sh', '-c', "docker ps --filter name=ballerina-sts" 131 | standardOutput = stdOut 132 | } 133 | if (stdOut.toString().contains("ballerina-sts")) { 134 | println "Stopping Ballerina STS." 135 | exec { 136 | commandLine 'sh', '-c', "docker stop ballerina-sts" 137 | standardOutput = stdOut 138 | } 139 | println stdOut.toString() 140 | println "Waiting 15s until the Ballerina STS get stopped." 141 | sleep(15 * 1000) 142 | } else { 143 | println "Ballerina STS is not started." 144 | } 145 | } 146 | } 147 | } 148 | stopBallerinaSTS.onlyIf { !project.gradle.startParameter.excludedTaskNames.contains('test') } 149 | 150 | updateTomlFiles.dependsOn copyStdlibs 151 | 152 | 153 | test.finalizedBy stopBallerinaSTS 154 | test.dependsOn startBallerinaSTS 155 | test.dependsOn ":${packageName}-native:build" 156 | test.dependsOn ":${packageName}-compiler-plugin:build" 157 | 158 | build.dependsOn ":${packageName}-compiler-plugin:build" 159 | build.dependsOn "generatePomFileForMavenPublication" 160 | build.finalizedBy stopBallerinaSTS 161 | build.dependsOn startBallerinaSTS 162 | build.dependsOn ":${packageName}-native:build" 163 | 164 | publishToMavenLocal.dependsOn build 165 | publish.dependsOn build 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Ballerina JWT Library 2 | =================== 3 | 4 | [![Build](https://github.com/ballerina-platform/module-ballerina-jwt/actions/workflows/build-timestamped-master.yml/badge.svg)](https://github.com/ballerina-platform/module-ballerina-jwt/actions/workflows/build-timestamped-master.yml) 5 | [![codecov](https://codecov.io/gh/ballerina-platform/module-ballerina-jwt/branch/master/graph/badge.svg)](https://codecov.io/gh/ballerina-platform/module-ballerina-jwt) 6 | [![Trivy](https://github.com/ballerina-platform/module-ballerina-jwt/actions/workflows/trivy-scan.yml/badge.svg)](https://github.com/ballerina-platform/module-ballerina-jwt/actions/workflows/trivy-scan.yml) 7 | [![GraalVM Check](https://github.com/ballerina-platform/module-ballerina-jwt/actions/workflows/build-with-bal-test-graalvm.yml/badge.svg)](https://github.com/ballerina-platform/module-ballerina-jwt/actions/workflows/build-with-bal-test-graalvm.yml) 8 | [![GitHub Last Commit](https://img.shields.io/github/last-commit/ballerina-platform/module-ballerina-jwt.svg?label=Last%20Commit)](https://github.com/ballerina-platform/module-ballerina-jwt/commits/master) 9 | [![GitHub issues](https://img.shields.io/github/issues/ballerina-platform/ballerina-standard-library/module/jwt.svg?label=Open%20Issues)](https://github.com/ballerina-platform/ballerina-standard-library/labels/module%2Fjwt) 10 | 11 | This library provides a framework for authentication/authorization with JWTs and generation/validation of JWTs as specified in the [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519), [RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515), and [RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517). 12 | 13 | JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure enabling the claims to be signed digitally or protecting the integrity with a Message Authentication Code(MAC) and/or encrypted. 14 | 15 | The Ballerina `jwt` library facilitates auth providers that are to be used by the clients and listeners of different protocol connectors. Also, it provides the APIs for issuing a self-signed JWT and validating a JWT. 16 | 17 | ### Listener JWT Auth provider 18 | 19 | Represents the listener JWT Auth provider, which is used to authenticate the provided credentials (JWT) against the provided JWT validator configurations. 20 | 21 | ### Client JWT Auth provider 22 | 23 | Represents the client JWT Auth provider, which is used to authenticate with an external endpoint by issuing a self-signed JWT against the provided JWT issuer configurations. 24 | 25 | ### JWT issuer 26 | 27 | A self-signed JWT can be issued with the provided configurations using this API as follows: 28 | 29 | ```ballerina 30 | jwt:IssuerConfig issuerConfig = { 31 | username: "ballerina", 32 | issuer: "wso2", 33 | audience: "vEwzbcasJVQm1jVYHUHCjhxZ4tYa", 34 | expTime: 3600, 35 | signatureConfig: { 36 | config: { 37 | keyFile: "/path/to/private.key" 38 | } 39 | } 40 | }; 41 | 42 | string jwt = check jwt:issue(issuerConfig); 43 | ``` 44 | 45 | ### JWT validator 46 | 47 | A JWT can be validated with the provided configurations using the API as follows: 48 | 49 | ```ballerina 50 | string jwt = "eyJ0eXAiOiJKV1QiLA0KI[...omitted for brevity...]mB92K27uhbwW1gFWFOEjXk"; 51 | 52 | jwt:ValidatorConfig validatorConfig = { 53 | issuer: "wso2", 54 | audience: "vEwzbcasJVQm1jVYHUHCjhxZ4tYa", 55 | clockSkew: 60, 56 | signatureConfig: { 57 | certFile: "/path/to/public.crt" 58 | } 59 | }; 60 | 61 | jwt:Payload result = check jwt:validate(jwt, validatorConfig); 62 | ``` 63 | 64 | ## Issues and projects 65 | 66 | Issues and Projects tabs are disabled for this repository as this is part of the Ballerina Standard Library. To report bugs, request new features, start new discussions, view project boards, etc., go to the [Ballerina Standard Library parent repository](https://github.com/ballerina-platform/ballerina-standard-library). 67 | 68 | This repository only contains the source code for the module. 69 | 70 | ## Build from the source 71 | 72 | ### Set up the prerequisites 73 | 74 | 1. Download and install Java SE Development Kit (JDK) version 21 (from one of the following locations). 75 | 76 | * [Oracle](https://www.oracle.com/java/technologies/downloads/) 77 | 78 | * [OpenJDK](https://adoptium.net) 79 | 80 | > **Note:** Set the `JAVA_HOME` environment variable to the path name of the directory into which you installed JDK. 81 | 82 | 2. Export your GitHub Personal Access Token (PAT) with the `read package` permission as follows: 83 | 84 | ``` 85 | export packageUser= 86 | export packagePAT= 87 | ``` 88 | 89 | 3. Download and install [Docker](https://www.docker.com/). 90 | 91 | ### Build the source 92 | 93 | Execute the commands below to build from the source. 94 | 95 | 1. To build the package: 96 | ``` 97 | ./gradlew clean build 98 | ``` 99 | 2. To run the tests: 100 | ``` 101 | ./gradlew clean test 102 | ``` 103 | 104 | 3. To run a group of tests 105 | ``` 106 | ./gradlew clean test -Pgroups= 107 | ``` 108 | 109 | 4. To build the without the tests: 110 | ``` 111 | ./gradlew clean build -x test 112 | ``` 113 | 114 | 5. To debug package implementation: 115 | ``` 116 | ./gradlew clean build -Pdebug= 117 | ``` 118 | 119 | 6. To debug with Ballerina language: 120 | ``` 121 | ./gradlew clean build -PbalJavaDebug= 122 | ``` 123 | 124 | 7. Publish the generated artifacts to the local Ballerina central repository: 125 | ``` 126 | ./gradlew clean build -PpublishToLocalCentral=true 127 | ``` 128 | 129 | 8. Publish the generated artifacts to the Ballerina central repository: 130 | ``` 131 | ./gradlew clean build -PpublishToCentral=true 132 | ``` 133 | 134 | ## Contribute to Ballerina 135 | 136 | As an open source project, Ballerina welcomes contributions from the community. 137 | 138 | For more information, go to the [contribution guidelines](https://github.com/ballerina-platform/ballerina-lang/blob/master/CONTRIBUTING.md). 139 | 140 | ## Code of conduct 141 | 142 | All contributors are encouraged to read the [Ballerina Code of Conduct](https://ballerina.io/code-of-conduct). 143 | 144 | ## Useful links 145 | 146 | * For more information go to the [`jwt` library](https://lib.ballerina.io/ballerina/jwt/latest). 147 | * For example demonstrations of the usage, go to [Ballerina By Examples](https://ballerina.io/learn/by-example/). 148 | * Chat live with us via our [Discord server](https://discord.gg/ballerinalang). 149 | * Post all technical questions on Stack Overflow with the [#ballerina](https://stackoverflow.com/questions/tagged/ballerina) tag. 150 | -------------------------------------------------------------------------------- /compiler-plugin-tests/src/test/java/io/ballerina/stdlib/jwt/compiler/staticcodeanalyzer/StaticCodeAnalyzerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.org) 3 | * 4 | * WSO2 LLC. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | package io.ballerina.stdlib.jwt.compiler.staticcodeanalyzer; 20 | 21 | import com.fasterxml.jackson.databind.JsonNode; 22 | import com.fasterxml.jackson.databind.ObjectMapper; 23 | import com.fasterxml.jackson.databind.SerializationFeature; 24 | import io.ballerina.projects.Project; 25 | import io.ballerina.projects.ProjectEnvironmentBuilder; 26 | import io.ballerina.projects.directory.BuildProject; 27 | import io.ballerina.projects.environment.Environment; 28 | import io.ballerina.projects.environment.EnvironmentBuilder; 29 | import io.ballerina.scan.Issue; 30 | import io.ballerina.scan.Rule; 31 | import io.ballerina.scan.Source; 32 | import io.ballerina.scan.test.Assertions; 33 | import io.ballerina.scan.test.TestOptions; 34 | import io.ballerina.scan.test.TestRunner; 35 | import org.testng.Assert; 36 | import org.testng.annotations.Test; 37 | 38 | import java.io.ByteArrayOutputStream; 39 | import java.io.IOException; 40 | import java.io.PrintStream; 41 | import java.nio.file.Files; 42 | import java.nio.file.Path; 43 | import java.nio.file.Paths; 44 | import java.util.Arrays; 45 | import java.util.List; 46 | import java.util.Locale; 47 | import java.util.stream.Collectors; 48 | 49 | import static io.ballerina.scan.RuleKind.VULNERABILITY; 50 | import static io.ballerina.stdlib.jwt.compiler.staticcodeanalyzer.JwtRule.AVOID_WEAK_CIPHER_ALGORITHMS; 51 | import static java.nio.charset.StandardCharsets.UTF_8; 52 | 53 | public class StaticCodeAnalyzerTest { 54 | private static final Path RESOURCE_PACKAGES_DIRECTORY = Paths 55 | .get("src", "test", "resources", "static_code_analyzer", "ballerina_packages").toAbsolutePath(); 56 | private static final Path EXPECTED_OUTPUT_DIRECTORY = Paths 57 | .get("src", "test", "resources", "static_code_analyzer", "expected_output").toAbsolutePath(); 58 | private static final Path JSON_RULES_FILE_PATH = Paths 59 | .get("../", "compiler-plugin", "src", "main", "resources", "rules.json").toAbsolutePath(); 60 | private static final Path DISTRIBUTION_PATH = Paths.get("../", "target", "ballerina-runtime"); 61 | private static final String MODULE_BALLERINA_JWT = "module-ballerina-jwt"; 62 | 63 | @Test 64 | public void validateRulesJson() throws IOException { 65 | String expectedRules = "[" + Arrays.stream(JwtRule.values()) 66 | .map(JwtRule::toString).collect(Collectors.joining(",")) + "]"; 67 | String actualRules = Files.readString(JSON_RULES_FILE_PATH); 68 | assertJsonEqual(actualRules, expectedRules); 69 | } 70 | 71 | @Test 72 | public void testStaticCodeRulesWithAPI() throws IOException { 73 | ByteArrayOutputStream console = new ByteArrayOutputStream(); 74 | PrintStream printStream = new PrintStream(console, true, UTF_8); 75 | 76 | for (JwtRule rule : JwtRule.values()) { 77 | testIndividualRule(rule, console, printStream); 78 | } 79 | } 80 | 81 | private void testIndividualRule(JwtRule rule, ByteArrayOutputStream console, PrintStream printStream) 82 | throws IOException { 83 | String targetPackageName = "rule" + rule.getId(); 84 | Path targetPackagePath = RESOURCE_PACKAGES_DIRECTORY.resolve(targetPackageName); 85 | 86 | TestRunner testRunner = setupTestRunner(targetPackagePath, printStream); 87 | testRunner.performScan(); 88 | 89 | validateRules(testRunner.getRules()); 90 | validateIssues(rule, testRunner.getIssues()); 91 | validateOutput(console, targetPackageName); 92 | 93 | console.reset(); 94 | } 95 | 96 | private TestRunner setupTestRunner(Path targetPackagePath, PrintStream printStream) { 97 | Project project = BuildProject.load(getEnvironmentBuilder(), targetPackagePath); 98 | TestOptions options = TestOptions.builder(project).setOutputStream(printStream).build(); 99 | return new TestRunner(options); 100 | } 101 | 102 | private void validateRules(List rules) { 103 | Assertions.assertRule( 104 | rules, 105 | "ballerina/jwt:1", 106 | AVOID_WEAK_CIPHER_ALGORITHMS.getDescription(), 107 | VULNERABILITY); 108 | } 109 | 110 | private void validateIssues(JwtRule rule, List issues) { 111 | switch (rule) { 112 | case AVOID_WEAK_CIPHER_ALGORITHMS: 113 | Assert.assertEquals(issues.size(), 10); 114 | Assertions.assertIssue(issues, 0, "ballerina/jwt:1", "function_named_arg_capture_pattern.bal", 115 | 27, 27, Source.BUILT_IN); 116 | Assertions.assertIssue(issues, 1, "ballerina/jwt:1", "function_named_arg_list_pattern.bal", 117 | 29, 29, Source.BUILT_IN); 118 | Assertions.assertIssue(issues, 2, "ballerina/jwt:1", "function_pos_arg_capture_pattern.bal", 119 | 27, 27, Source.BUILT_IN); 120 | Assertions.assertIssue(issues, 3, "ballerina/jwt:1", "function_pos_arg_list_pattern.bal", 121 | 29, 29, Source.BUILT_IN); 122 | Assertions.assertIssue(issues, 4, "ballerina/jwt:1", "inline_named_arg.bal", 123 | 19, 25, Source.BUILT_IN); 124 | Assertions.assertIssue(issues, 5, "ballerina/jwt:1", "inline_pos_arg.bal", 125 | 19, 25, Source.BUILT_IN); 126 | Assertions.assertIssue(issues, 6, "ballerina/jwt:1", "module_named_arg_capture_pattern.bal", 127 | 27, 27, Source.BUILT_IN); 128 | Assertions.assertIssue(issues, 7, "ballerina/jwt:1", "module_named_arg_list_pattern.bal", 129 | 29, 29, Source.BUILT_IN); 130 | Assertions.assertIssue(issues, 8, "ballerina/jwt:1", "module_pos_arg_capture_pattern.bal", 131 | 27, 27, Source.BUILT_IN); 132 | Assertions.assertIssue(issues, 9, "ballerina/jwt:1", "module_pos_arg_list_pattern.bal", 133 | 29, 29, Source.BUILT_IN); 134 | break; 135 | default: 136 | Assert.fail("Unhandled rule in validateIssues: " + rule); 137 | break; 138 | } 139 | } 140 | 141 | private void validateOutput(ByteArrayOutputStream console, String targetPackageName) throws IOException { 142 | String output = console.toString(UTF_8); 143 | String jsonOutput = extractJson(output); 144 | String expectedOutput = Files.readString(EXPECTED_OUTPUT_DIRECTORY.resolve(targetPackageName + ".json")); 145 | assertJsonEqual(jsonOutput, expectedOutput); 146 | } 147 | 148 | private static ProjectEnvironmentBuilder getEnvironmentBuilder() { 149 | Environment environment = EnvironmentBuilder.getBuilder().setBallerinaHome(DISTRIBUTION_PATH).build(); 150 | return ProjectEnvironmentBuilder.getBuilder(environment); 151 | } 152 | 153 | private String extractJson(String consoleOutput) { 154 | int startIndex = consoleOutput.indexOf("["); 155 | int endIndex = consoleOutput.lastIndexOf("]"); 156 | if (startIndex == -1 || endIndex == -1) { 157 | return ""; 158 | } 159 | return consoleOutput.substring(startIndex, endIndex + 1); 160 | } 161 | 162 | private void assertJsonEqual(String actual, String expected) { 163 | Assert.assertEquals(normalizeString(actual), normalizeString(expected)); 164 | } 165 | 166 | private static String normalizeString(String json) { 167 | try { 168 | ObjectMapper mapper = new ObjectMapper().configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true); 169 | JsonNode node = mapper.readTree(json); 170 | String normalizedJson = mapper.writeValueAsString(node) 171 | .replaceAll(":\".*" + MODULE_BALLERINA_JWT, ":\"" + MODULE_BALLERINA_JWT); 172 | return isWindows() ? normalizedJson.replace("/", "\\\\") : normalizedJson; 173 | } catch (Exception ignore) { 174 | return json; 175 | } 176 | } 177 | 178 | private static boolean isWindows() { 179 | return System.getProperty("os.name").toLowerCase(Locale.ENGLISH).startsWith("windows"); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /ballerina/tests/test_utils.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/test; 18 | 19 | const string KEYSTORE_PATH = "tests/resources/keystore/ballerinaKeystore.p12"; 20 | const string TRUSTSTORE_PATH = "tests/resources/keystore/ballerinaTruststore.p12"; 21 | const string PRIVATE_KEY_PATH = "tests/resources/key/private.key"; 22 | const string ENCRYPTED_PRIVATE_KEY_PATH = "tests/resources/key/encryptedPrivate.key"; 23 | const string PUBLIC_CERT_PATH = "tests/resources/cert/public.crt"; 24 | const string INVALID_PUBLIC_CERT_PATH = "tests/resources/cert/invalidPublic.crt"; 25 | 26 | // { 27 | // "alg": "RS256", 28 | // "typ": "JWT", 29 | // "kid": "5a0b754-895f-4279-8843-b745e11a57e9" 30 | // } 31 | // { 32 | // "iss": "wso2", 33 | // "sub": "John", 34 | // "aud": [ 35 | // "ballerina", 36 | // "ballerinaSamples" 37 | // ], 38 | // "jti": "JlbmMiOiJBMTI4Q0JDLUhTMjU2In", 39 | // "exp": 1935400394, 40 | // "nbf": 1620040394, 41 | // "iat": 1620040394, 42 | // "scp": "hello" 43 | // } 44 | const string JWT1 = "eyJhbGciOiJSUzI1NiIsICJ0eXAiOiJKV1QiLCAia2lkIjoiNWEwYjc1NC04OTVmLTQyNzktODg0My1iNzQ1ZTExYTU3ZTk" + 45 | "ifQ.eyJpc3MiOiJ3c28yIiwgInN1YiI6IkpvaG4iLCAiYXVkIjpbImJhbGxlcmluYSIsICJiYWxsZXJpbmFTYW1wbGVzIl0" + 46 | "sICJqdGkiOiJKbGJtTWlPaUpCTVRJNFEwSkRMVWhUTWpVMkluIiwgImV4cCI6MTkzNTQwMDM5NCwgIm5iZiI6MTYyMDA0MD" + 47 | "M5NCwgImlhdCI6MTYyMDA0MDM5NCwgInNjcCI6ImhlbGxvIn0.I9XAfgOkIRor-PTshCzikw92N6jNNPmwXsUTr0OI6bMmG" + 48 | "oEoHw0Uo1g3zOfgKOFfIN0bVqUs9gcg05biojIqFmMErrfG4h1DIIym4PLXWzQ-JWPNnMFYSAC5C84MvBPU5kYMnNbVSTsp" + 49 | "SbQXocY0FHe2_GvhdwHDviRPMS3RnkJxRVORD9BF4DLJuQJdEJUbT_iYSTCd7ay88oCEgm7KGTDKy66-JqC7FAppc7mj7Lk" + 50 | "N48T26BW0aC5wN2LJkYql2H3ONewHOTuEFyH6cZl7dfm66hZiryqMK1BIMwecMscUqKof1h8cHWZ4BDeccCJ5vWNe0SHTrx" + 51 | "3AWPcXnRd0Vw"; 52 | 53 | // { 54 | // "alg": "RS256", 55 | // "typ": "JWT", 56 | // "kid": "NTAxZmMxNDMyZDg3MTU1ZGM0MzEzODJhZWI4NDNlZDU1OGFkNjFiMQ" 57 | // } 58 | // { 59 | // "sub": "admin", 60 | // "iss": "ballerina", 61 | // "exp": 1907665746, 62 | // "jti": "100078234ba23", 63 | // "aud": [ 64 | // "vEwzbcasJVQm1jVYHUHCjhxZ4tYa" 65 | // ] 66 | // } 67 | const string JWT2 = "eyJhbGciOiJSUzI1NiIsICJ0eXAiOiJKV1QiLCAia2lkIjoiTlRBeFptTXhORE15WkRnM01UVTFaR00wTXpFek9ESmhaV0k" + 68 | "0TkRObFpEVTFPR0ZrTmpGaU1RIn0.eyJzdWIiOiJhZG1pbiIsICJpc3MiOiJiYWxsZXJpbmEiLCAiZXhwIjoxOTA3NjY1Nz" + 69 | "Q2LCAianRpIjoiMTAwMDc4MjM0YmEyMyIsICJhdWQiOlsidkV3emJjYXNKVlFtMWpWWUhVSENqaHhaNHRZYSJdfQ.E8E7VO" + 70 | "18V6MG7Ns87Y314Dqg8RYOMe0WWYlSYFhSv0mHkJQ8bSSyBJzFG0Se_7UsTWFBwzIALw6wUiP7UGraosilf8k6HGJWbTjWt" + 71 | "LXfniJXx5NczikzciG8ADddksm-0AMi5uPsgAQdg7FNaH9f4vAL6SPMEYp2gN6GDnWTH7M1vnknwjOwTbQpGrPu_w2V1tbs" + 72 | "BwSzof3Fk_cYrntu8D_pfsBu3eqFiJZD7AXUq8EYbgIxpSwvdi6_Rvw2_TAi46drouxXK2Jglz_HvheUVCERT15Y8JNJONJ" + 73 | "PJ52MsN6t297hyFV9AmyNPzwHxxmi753TclbapDqDnVPI1tpc-Q"; 74 | 75 | // { 76 | // "alg": "RS384", 77 | // "typ": "JWT" 78 | // } 79 | // { 80 | // "iss": "wso2", 81 | // "sub": "John", 82 | // "aud": "ballerina", 83 | // "exp": 1935402506, 84 | // "nbf": 1620042506, 85 | // "iat": 1620042506 86 | // } 87 | const string JWT3 = "eyJhbGciOiJSUzM4NCIsICJ0eXAiOiJKV1QifQ.eyJpc3MiOiJ3c28yIiwgInN1YiI6IkpvaG4iLCAiYXVkIjoiYmFsbGVy" + 88 | "aW5hIiwgImV4cCI6MTkzNTQwMjU4NCwgIm5iZiI6MTYyMDA0MjU4NCwgImlhdCI6MTYyMDA0MjU4NH0.f9HjtIeprPxeYj_" + 89 | "_00pUf8TSS_mlNNvzoY8V8Agg27D4YquxVIj5QwbKjSZ7sdLKC_jlVEwyX0fp_YGcSSfoE-s3T_1wY2e36vxzm35CJG8Lcs" + 90 | "EHwjtMrilJ-CicHJKsz0QsPSJJTDJe490tmPMukh-z1Urm779gYnJroUzDcgEvnrsiLsJwTl7M_VmS56B-iXk7IFoId_6gX" + 91 | "kq3uA9upmyzV6C5C257W_ApMw8icRR8HS19w0NAu5ws_sxkoM6H3SlFqidZgZ0UvTXnvLaQgBaV0RZX8ctzWOqj601vpVqh" + 92 | "qGUvTNGFnpd5ZugLKJ1XXs66ZWdfTkYi2NH_-8cK8Q"; 93 | 94 | // { 95 | // "alg": "RS512", 96 | // "typ": "JWT" 97 | // } 98 | // { 99 | // "iss": "wso2", 100 | // "sub": "John", 101 | // "aud": "ballerina", 102 | // "exp": 1935402506, 103 | // "nbf": 1620042506, 104 | // "iat": 1620042506 105 | // } 106 | const string JWT4 = "eyJhbGciOiJSUzUxMiIsICJ0eXAiOiJKV1QifQ.eyJpc3MiOiJ3c28yIiwgInN1YiI6IkpvaG4iLCAiYXVkIjoiYmFsbGVy" + 107 | "aW5hIiwgImV4cCI6MTkzNTQwMjUwNiwgIm5iZiI6MTYyMDA0MjUwNiwgImlhdCI6MTYyMDA0MjUwNn0.R6SobPWHg4z8vY4" + 108 | "OV8MRNocIbQV3tHrgD9MzJeuifofsCRMLAhjhKtldShxA9BnRegmawV6Hqn0dBHgrEW69ydx-frE3k8-u9LufH82Lb5JKb5" + 109 | "ZUm5Zme5PVru628py_e1TGqKyDjMLRwEZUYYxIMV5nmDO6705XDH2sxRSgGBXkJxMGoycnj1UYRLvU7q315js6DKvXY7Yfa" + 110 | "VvO0_xTlFs4381lTXRjkK2G1XMjZQQZK7Px4qSBLqvr9uVPHbezlVKwQs4b1jIgwsE4Fx-bjti6tWCV4NhcU4WhoYB8pYkv" + 111 | "f4WrBpKhcaZQeAiCUA6bUsqSkewvDwtjL9mUs0OkCw"; 112 | 113 | // { 114 | // "alg": "RS256", 115 | // "typ": "JWT", 116 | // "kid": "5a0b754-895f-4279-8843-b745e11a57e9" 117 | // } 118 | // { 119 | // "iss": "wso2", 120 | // "sub": "John", 121 | // "aud": [ 122 | // "ballerina", 123 | // "ballerinaSamples" 124 | // ], 125 | // "jti": "JlbmMiOiJBMTI4Q0JDLUhTMjU2In", 126 | // "exp": 1940845451, 127 | // "nbf": 1625485451, 128 | // "iat": 1625485451, 129 | // "scp": "hello" 130 | // } 131 | const string JWT5 = "eyJhbGciOiJSUzI1NiIsICJ0eXAiOiJKV1QiLCAia2lkIjoiNWEwYjc1NC04OTVmLTQyNzktODg0My1iNzQ1ZTExYTU3ZTk" + 132 | "ifQ.eyJpc3MiOiJ3c28yIiwgInN1YiI6IkpvaG4iLCAiYXVkIjpbImJhbGxlcmluYSIsICJiYWxsZXJpbmFTYW1wbGVzIl0" + 133 | "sICJqdGkiOiJKbGJtTWlPaUpCTVRJNFEwSkRMVWhUTWpVMkluIiwgImV4cCI6MTk0MDg0NTQ1MSwgIm5iZiI6MTYyNTQ4NT" + 134 | "Q1MSwgImlhdCI6MTYyNTQ4NTQ1MSwgInNjcCI6ImhlbGxvIn0.G0c9t9AE7BmYhKCNY4DsYmypk4XDQ-lNqpENYUouugWXn" + 135 | "t3-d2rpLHxnXNqcIqPBWRV6QuxO-0jcp6LjkjO2khgZ5jMVNFtlyUN4cQ9UEHfwiPAjSlkwT7sAEHPUd8S8wp714eWtkdjo" + 136 | "ysgpcdEE3VJi1OgI1SeVDiN6l7jkt-xxhMsbHGYIeTc1lTLgwNtCJaNJKHvi0uJu5x9YfUznett8Dw465DbADhBLMoSBYAT" + 137 | "t4flzCBsTGWC7XZaFnwT4mUlX7WpTOgv1Nsq5GVLszvsnzs6BE__Mvr4zl5pdChVbkMXX3US6fYguK268XKjzgtpMVxUpL3" + 138 | "CrzwQpIRyI-Q"; 139 | 140 | // { 141 | // "alg": "HS256", 142 | // "typ": "JWT" 143 | // } 144 | // { 145 | // "iss": "wso2", 146 | // "sub": "John", 147 | // "aud": [ 148 | // "ballerina", 149 | // "ballerinaSamples" 150 | // ], 151 | // "exp": 1943255429, 152 | // "nbf": 1627895429, 153 | // "iat": 1627895429 154 | // } 155 | const string JWT6 = "eyJhbGciOiJIUzI1NiIsICJ0eXAiOiJKV1QifQ.eyJpc3MiOiJ3c28yIiwgInN1YiI6IkpvaG4iLCAiYXVkIjpbImJhbGxl" + 156 | "cmluYSIsICJiYWxsZXJpbmFTYW1wbGVzIl0sICJleHAiOjE5NDMyNTU0MjksICJuYmYiOjE2Mjc4OTU0MjksICJpYXQiOjE" + 157 | "2Mjc4OTU0Mjl9.BqFvZtKFj4KiVGkSkbXAcG4mpmSnGM0f60GYMw7dj4k"; 158 | 159 | // { 160 | // "alg": "HS384", 161 | // "typ": "JWT" 162 | // } 163 | // { 164 | // "iss": "wso2", 165 | // "sub": "John", 166 | // "aud": [ 167 | // "ballerina", 168 | // "ballerinaSamples" 169 | // ], 170 | // "exp": 1943257494, 171 | // "nbf": 1627897494, 172 | // "iat": 1627897494 173 | // } 174 | const string JWT7 = "eyJhbGciOiJIUzM4NCIsICJ0eXAiOiJKV1QifQ.eyJpc3MiOiJ3c28yIiwgInN1YiI6IkpvaG4iLCAiYXVkIjpbImJhbGxl" + 175 | "cmluYSIsICJiYWxsZXJpbmFTYW1wbGVzIl0sICJleHAiOjE5NDMyNTc0OTQsICJuYmYiOjE2Mjc4OTc0OTQsICJpYXQiOjE" + 176 | "2Mjc4OTc0OTR9.gwyn7kbaX-AQi0SQQmPoKfazehSsr7XUTnxewKny2qcOOVJrIqlLVyCoyacFlPel"; 177 | 178 | // { 179 | // "alg": "HS512", 180 | // "typ": "JWT" 181 | // } 182 | // { 183 | // "iss": "wso2", 184 | // "sub": "John", 185 | // "aud": [ 186 | // "ballerina", 187 | // "ballerinaSamples" 188 | // ], 189 | // "exp": 1944016504, 190 | // "nbf": 1628656504, 191 | // "iat": 1628656504 192 | // } 193 | const string JWT8 = "eyJhbGciOiJIUzUxMiIsICJ0eXAiOiJKV1QifQ.eyJpc3MiOiJ3c28yIiwgInN1YiI6IkpvaG4iLCAiYXVkIjpbImJhbGxl" + 194 | "cmluYSIsICJiYWxsZXJpbmFTYW1wbGVzIl0sICJleHAiOjE5NDQwMTY1MDQsICJuYmYiOjE2Mjg2NTY1MDQsICJpYXQiOjE" + 195 | "2Mjg2NTY1MDR9.pXPpwlNyqvNdkmDhYYwhKGArcZXbMz9sqXpv-I05Nox65Q0tMDkzSIK-jpkrp-yag737v4GckS8MuZuym" + 196 | "ee43Q"; 197 | 198 | // Builds the complete error message by evaluating all the inner causes and asserts the inclusion. 199 | isolated function assertContains(error err, string text) { 200 | string message = err.message(); 201 | error? cause = err.cause(); 202 | while cause is error { 203 | message += " " + cause.message(); 204 | cause = cause.cause(); 205 | } 206 | test:assertTrue(message.includes(text)); 207 | } 208 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 87 | 88 | # Use the maximum available, or set MAX_FD != -1 to use that value. 89 | MAX_FD=maximum 90 | 91 | warn () { 92 | echo "$*" 93 | } >&2 94 | 95 | die () { 96 | echo 97 | echo "$*" 98 | echo 99 | exit 1 100 | } >&2 101 | 102 | # OS specific support (must be 'true' or 'false'). 103 | cygwin=false 104 | msys=false 105 | darwin=false 106 | nonstop=false 107 | case "$( uname )" in #( 108 | CYGWIN* ) cygwin=true ;; #( 109 | Darwin* ) darwin=true ;; #( 110 | MSYS* | MINGW* ) msys=true ;; #( 111 | NONSTOP* ) nonstop=true ;; 112 | esac 113 | 114 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 115 | 116 | 117 | # Determine the Java command to use to start the JVM. 118 | if [ -n "$JAVA_HOME" ] ; then 119 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 120 | # IBM's JDK on AIX uses strange locations for the executables 121 | JAVACMD=$JAVA_HOME/jre/sh/java 122 | else 123 | JAVACMD=$JAVA_HOME/bin/java 124 | fi 125 | if [ ! -x "$JAVACMD" ] ; then 126 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 127 | 128 | Please set the JAVA_HOME variable in your environment to match the 129 | location of your Java installation." 130 | fi 131 | else 132 | JAVACMD=java 133 | if ! command -v java >/dev/null 2>&1 134 | then 135 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 136 | 137 | Please set the JAVA_HOME variable in your environment to match the 138 | location of your Java installation." 139 | fi 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 147 | # shellcheck disable=SC3045 148 | MAX_FD=$( ulimit -H -n ) || 149 | warn "Could not query maximum file descriptor limit" 150 | esac 151 | case $MAX_FD in #( 152 | '' | soft) :;; #( 153 | *) 154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 155 | # shellcheck disable=SC3045 156 | ulimit -n "$MAX_FD" || 157 | warn "Could not set maximum file descriptor limit to $MAX_FD" 158 | esac 159 | fi 160 | 161 | # Collect all arguments for the java command, stacking in reverse order: 162 | # * args from the command line 163 | # * the main class name 164 | # * -classpath 165 | # * -D...appname settings 166 | # * --module-path (only if needed) 167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 168 | 169 | # For Cygwin or MSYS, switch paths to Windows format before running java 170 | if "$cygwin" || "$msys" ; then 171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 173 | 174 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 175 | 176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 177 | for arg do 178 | if 179 | case $arg in #( 180 | -*) false ;; # don't mess with options #( 181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 182 | [ -e "$t" ] ;; #( 183 | *) false ;; 184 | esac 185 | then 186 | arg=$( cygpath --path --ignore --mixed "$arg" ) 187 | fi 188 | # Roll the args list around exactly as many times as the number of 189 | # args, so each arg winds up back in the position where it started, but 190 | # possibly modified. 191 | # 192 | # NB: a `for` loop captures its iteration list before it begins, so 193 | # changing the positional parameters here affects neither the number of 194 | # iterations, nor the values presented in `arg`. 195 | shift # remove old arg 196 | set -- "$@" "$arg" # push replacement arg 197 | done 198 | fi 199 | 200 | 201 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 202 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 203 | 204 | # Collect all arguments for the java command; 205 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 206 | # shell script including quotes and variable substitutions, so put them in 207 | # double quotes to make sure that they get re-expanded; and 208 | # * put everything else in single quotes, so that it's not re-expanded. 209 | 210 | set -- \ 211 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 212 | -classpath "$CLASSPATH" \ 213 | org.gradle.wrapper.GradleWrapperMain \ 214 | "$@" 215 | 216 | # Stop when "xargs" is not available. 217 | if ! command -v xargs >/dev/null 2>&1 218 | then 219 | die "xargs is not available" 220 | fi 221 | 222 | # Use "xargs" to parse quoted args. 223 | # 224 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 225 | # 226 | # In Bash we could simply go: 227 | # 228 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 229 | # set -- "${ARGS[@]}" "$@" 230 | # 231 | # but POSIX shell has neither arrays nor command substitution, so instead we 232 | # post-process each arg (as a line of input to sed) to backslash-escape any 233 | # character that might be a shell metacharacter, then use eval to reverse 234 | # that process (while maintaining the separation between arguments), and wrap 235 | # the whole thing up as a single "set" statement. 236 | # 237 | # This will of course break if any of these variables contains a newline or 238 | # an unmatched quote. 239 | # 240 | 241 | eval "set -- $( 242 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 243 | xargs -n1 | 244 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 245 | tr '\n' ' ' 246 | )" '"$@"' 247 | 248 | exec "$JAVACMD" "$@" 249 | -------------------------------------------------------------------------------- /ballerina/jwt_issuer.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/crypto; 18 | import ballerina/time; 19 | 20 | # Represents JWT issuer configurations. 21 | # 22 | # + issuer - JWT issuer, which is mapped to the `iss` 23 | # + username - JWT username, which is mapped to the `sub` 24 | # + audience - JWT audience, which is mapped to the `aud` 25 | # + jwtId - JWT ID, which is mapped to the `jti` 26 | # + keyId - JWT key ID, which is mapped the `kid` 27 | # + customClaims - Map of custom claims 28 | # + expTime - Expiry time in seconds 29 | # + signatureConfig - JWT signature configurations 30 | public type IssuerConfig record {| 31 | string issuer?; 32 | string username?; 33 | string|string[] audience?; 34 | string jwtId?; 35 | string keyId?; 36 | map customClaims?; 37 | decimal expTime = 300; 38 | IssuerSignatureConfig signatureConfig?; 39 | |}; 40 | 41 | # Represents JWT signature configurations. 42 | # 43 | # + algorithm - Cryptographic signing algorithm for JWS 44 | # + config - KeyStore configurations, private key configurations, `crypto:PrivateKey` or shared key configurations 45 | public type IssuerSignatureConfig record {| 46 | SigningAlgorithm algorithm = RS256; 47 | record {| 48 | crypto:KeyStore keyStore; 49 | string keyAlias; 50 | string keyPassword; 51 | |} | record {| 52 | string keyFile; 53 | string keyPassword?; 54 | |} | crypto:PrivateKey | string config?; 55 | |}; 56 | 57 | # Issues a JWT based on the provided configurations. JWT will be signed (JWS) if `crypto:KeyStore` information is 58 | # provided in the `jwt:KeyStoreConfig` and the `jwt:SigningAlgorithm` is not `jwt:NONE`. 59 | # ```ballerina 60 | # string jwt = check jwt:issue(issuerConfig); 61 | # ``` 62 | # 63 | # + issuerConfig - JWT issuer configurations 64 | # + return - JWT as a `string` or else a `jwt:Error` if an error occurred 65 | public isolated function issue(IssuerConfig issuerConfig) returns string|Error { 66 | Header header = prepareHeader(issuerConfig); 67 | Payload payload = preparePayload(issuerConfig); 68 | string headerString = check buildHeaderString(header); 69 | string payloadString = check buildPayloadString(payload); 70 | string jwtAssertion = headerString + "." + payloadString; 71 | 72 | IssuerSignatureConfig? issuerSignatureConfig = issuerConfig?.signatureConfig; 73 | if issuerSignatureConfig is () { 74 | return jwtAssertion; 75 | } 76 | IssuerSignatureConfig signatureConfig = issuerSignatureConfig; 77 | SigningAlgorithm algorithm = signatureConfig.algorithm; 78 | if algorithm is NONE { 79 | return jwtAssertion; 80 | } 81 | var config = signatureConfig?.config; 82 | if config is () { 83 | return prepareError("Signing JWT requires keystore information or private key information."); 84 | } else if config is string { 85 | return hmacJwtAssertion(jwtAssertion, algorithm, config); 86 | } else if config?.keyStore is crypto:KeyStore { 87 | crypto:KeyStore keyStore = config?.keyStore; 88 | string keyAlias = config?.keyAlias; 89 | string keyPassword = config?.keyPassword; 90 | crypto:PrivateKey|crypto:Error privateKey = crypto:decodeRsaPrivateKeyFromKeyStore(keyStore, keyAlias, keyPassword); 91 | if privateKey is crypto:PrivateKey { 92 | return signJwtAssertion(jwtAssertion, algorithm, privateKey); 93 | } else { 94 | return prepareError("Failed to decode private key.", privateKey); 95 | } 96 | } else if config is crypto:PrivateKey { 97 | return signJwtAssertion(jwtAssertion, algorithm, config); 98 | } else { 99 | string keyFile = config?.keyFile; 100 | string? keyPassword = config?.keyPassword; 101 | crypto:PrivateKey|crypto:Error privateKey = crypto:decodeRsaPrivateKeyFromKeyFile(keyFile, keyPassword); 102 | if privateKey is crypto:PrivateKey { 103 | return signJwtAssertion(jwtAssertion, algorithm, privateKey); 104 | } else { 105 | return prepareError("Failed to decode private key.", privateKey); 106 | } 107 | } 108 | } 109 | 110 | isolated function signJwtAssertion(string jwtAssertion, SigningAlgorithm alg, crypto:PrivateKey privateKey) 111 | returns string|Error { 112 | match alg { 113 | RS256 => { 114 | byte[]|crypto:Error signature = crypto:signRsaSha256(jwtAssertion.toBytes(), privateKey); 115 | if signature is byte[] { 116 | return (jwtAssertion + "." + encodeBase64Url(signature)); 117 | } else { 118 | return prepareError("RSA private key signing failed for SHA256 algorithm.", signature); 119 | } 120 | } 121 | RS384 => { 122 | byte[]|crypto:Error signature = crypto:signRsaSha384(jwtAssertion.toBytes(), privateKey); 123 | if signature is byte[] { 124 | return (jwtAssertion + "." + encodeBase64Url(signature)); 125 | } else { 126 | return prepareError("RSA private key signing failed for SHA384 algorithm.", signature); 127 | } 128 | } 129 | RS512 => { 130 | byte[]|crypto:Error signature = crypto:signRsaSha512(jwtAssertion.toBytes(), privateKey); 131 | if signature is byte[] { 132 | return (jwtAssertion + "." + encodeBase64Url(signature)); 133 | } else { 134 | return prepareError("RSA private key signing failed for SHA512 algorithm.", signature); 135 | } 136 | } 137 | _ => { 138 | return prepareError("Unsupported signing algorithm '" + alg.toString() + "'."); 139 | } 140 | } 141 | } 142 | 143 | isolated function hmacJwtAssertion(string jwtAssertion, SigningAlgorithm alg, string secret) 144 | returns string|Error { 145 | match alg { 146 | HS256 => { 147 | byte[]|crypto:Error signature = crypto:hmacSha256(jwtAssertion.toBytes(), secret.toBytes()); 148 | if signature is byte[] { 149 | return (jwtAssertion + "." + encodeBase64Url(signature)); 150 | } else { 151 | return prepareError("HMAC secret key signing failed for SHA256 algorithm.", signature); 152 | } 153 | } 154 | HS384 => { 155 | byte[]|crypto:Error signature = crypto:hmacSha384(jwtAssertion.toBytes(), secret.toBytes()); 156 | if signature is byte[] { 157 | return (jwtAssertion + "." + encodeBase64Url(signature)); 158 | } else { 159 | return prepareError("HMAC secret key signing failed for SHA384 algorithm.", signature); 160 | } 161 | } 162 | HS512 => { 163 | byte[]|crypto:Error signature = crypto:hmacSha512(jwtAssertion.toBytes(), secret.toBytes()); 164 | if signature is byte[] { 165 | return (jwtAssertion + "." + encodeBase64Url(signature)); 166 | } else { 167 | return prepareError("HMAC secret key signing failed for SHA512 algorithm.", signature); 168 | } 169 | } 170 | _ => { 171 | return prepareError("Unsupported signing algorithm '" + alg.toString() + "'."); 172 | } 173 | } 174 | } 175 | 176 | isolated function prepareHeader(IssuerConfig issuerConfig) returns Header { 177 | Header header = { alg: NONE, typ: "JWT" }; 178 | IssuerSignatureConfig? issuerSignatureConfig = issuerConfig?.signatureConfig; 179 | if issuerSignatureConfig is IssuerSignatureConfig { 180 | header.alg = issuerSignatureConfig.algorithm; 181 | } 182 | string? kid = issuerConfig?.keyId; 183 | if kid is string { 184 | header.kid = kid; 185 | } 186 | return header; 187 | } 188 | 189 | isolated function preparePayload(IssuerConfig issuerConfig) returns Payload { 190 | [int, decimal] currentTime = time:utcNow(); 191 | Payload payload = { 192 | exp: currentTime[0] + issuerConfig.expTime, 193 | iat: currentTime[0], 194 | nbf: currentTime[0] 195 | }; 196 | 197 | string? iss = issuerConfig?.issuer; 198 | if iss is string { 199 | payload.iss = iss; 200 | } 201 | string? sub = issuerConfig?.username; 202 | if sub is string { 203 | payload.sub = sub; 204 | } 205 | string|string[]? aud = issuerConfig?.audience; 206 | if aud is string || aud is string[] { 207 | payload.aud = aud; 208 | } 209 | string? jti = issuerConfig?.jwtId; 210 | if jti is string { 211 | payload.jti = jti; 212 | } 213 | 214 | map? customClaims = issuerConfig?.customClaims; 215 | if customClaims is map { 216 | foreach string key in customClaims.keys() { 217 | payload[key] = customClaims[key]; 218 | } 219 | } 220 | return payload; 221 | } 222 | 223 | isolated function buildHeaderString(Header header) returns string|Error { 224 | if !validateMandatoryHeaderFields(header) { 225 | return prepareError("Mandatory field signing algorithm (alg) is empty."); 226 | } 227 | return encodeBase64Url(header.toJsonString().toBytes()); 228 | } 229 | 230 | isolated function buildPayloadString(Payload payload) returns string|Error { 231 | return encodeBase64Url(payload.toJsonString().toBytes()); 232 | } 233 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------