diff --git a/README.md b/README.md index a6cc3b7..05bb4a1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -# Agama Security Key Project - [![Contributors][contributors-shield]][contributors-url] [![Forks][forks-shield]][forks-url] @@ -7,78 +5,36 @@ [![Issues][issues-shield]][issues-url] [![Apache License][license-shield]][license-url] -Use this project to authenticate using security devices (Yubico Key, Windows Hello, Touch ID on Mac, etc.) - -## How it works at a glance - -When a main flow of this project is launched (namely `io.jans.agama.securitykey.main`) the user's browser is redirected -to a view where he/she must first enter his/her username, then validate one of the security keys that he/she has -configured for his/her user (Yubico Key, Windows Hello, Touch ID on Mac, etc.). Finally, the user's browser is -redirected to the registered URI. - -> **Note:** You must have registered security devices to your user, to register you must use **Jans Casa**. - -## Project Deployment - -To deploy this project we need to meet the requirements. - -### Requirements - -1. Running instance of `Jans Auth Server`, `Jans Fido2` and `Jans Casa` - -### Add Java dependencies - -1. Download - latest [agama-securitykey-custom.jar](https://github.com/GluuFederation/agama-security-key/releases/latest/download/agama-securitykey-custom.jar) - from [Releases](https://github.com/GluuFederation/agama-securitykey/releases) -2. `scp` the jar file to `/opt/jans/jetty/jans-auth/custom/libs/` on Auth Server -3. On Auth Server, edit `/opt/jans/jetty/jans-auth/webapps/jans-auth.xml` and - add the jar file to the `...` element. For example: +# Agama Security Key -``` - - /jans-auth - - /jans-auth.war - - true - - ... - /opt/jans/jetty/jans-auth/custom/libs/agama-securitykey-custom.jar, - ... - - -``` +Welcome to the https://github.com/GluuFederation/agama-securitykey project. This project is governed by Gluu and published under an Apache 2.0 license. -4. Restart Auth Server to load the new jar: +Use this project to add user authentication with **SecurityKey**(Yubico Key, Windows Hello, Touch ID on Mac, etc.) -``` -systemctl restart jans-auth -```` +For more information you can also see +* [What is FIDO](https://fidoalliance.org/what-is-fido/) +* [How FIDO Works](https://fidoalliance.org/how-fido-works/) +* [FIDO Specs](https://www.w3.org/TR/webauthn-1) -### Deployment +## Supported IDPs -Download the -latest [agama-securitykey.gama](https://github.com/GluuFederation/agama-securitykey/releases/latest/download/agama-securitykey.gama) -file and deploy it in Auth Sever. +| IDP | Description | +|:-----------------|:-------------------------------------------------------------------| +| Jans Auth Server | [Deployment instructions](https://docs.jans.io/head/admin/install) | +| Gluu Flex | [Deployment instructions](https://docs.gluu.org/head/install) | -Siga los siguientes pasos: +## Flows -- Copy (SCP/SFTP) the gama file of this project to a location in your `Jans Server` -- Connect (SSH) to your `Jans Server` and open TUI: `python3 /opt/jans/jans-cli/jans_cli_tui.py` -- Navigate to the `Agama` tab and then select `"Upload project"`. Choose the gama file -- Wait for about one minute and then select the row in the table corresponding to this project -- Press `d` and ensure there were not deployment errors -- Pres `ESC` to close the dialog +| Qualified Name | Description | +|----------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `org.gluu.agama.securitykey.main` | This is the main flow that you can start directly from the browser. To use this first flow, make sure you have at least one `security key` configured, this can be done from **JANS CASA**. In this first view you will only be asked for your username and then you will be redirected to the `org.gluu.agama.passkey.fidoAuthn` flow where the validation of your security key device will be done. | +| `org.gluu.agama.securitykey.fidoAuthn` | This flow is used to perform the `security key` validation, you have the option to cancel the process. If you complete the flow successfully, you will be granted access. | -![TUI_DEPLOY](https://github.com/GluuFederation/agama-securitykey/assets/86965029/de25752e-3c86-4c67-a890-2e78494e4c6c) +## Configuration -## Testing +This agama project does not need any additional configuration to run its flow. -You'll need an OpenID Connect test RP. You can try [oidcdebugger](https://oidcdebugger.com/), -[jans-tarp](https://github.com/JanssenProject/jans/tree/main/demos/jans-tarp) -or [jans-tent](https://github.com/JanssenProject/jans/tree/main/demos/jans-tent). Check out this video to see an example -of **agama-securitykey** in action: +## Demo ![TEST_AGAMA_SECURITY_KEY](https://github.com/GluuFederation/agama-securitykey/assets/86965029/53baa0ab-d2d0-4e5f-a3c1-7c15c2dc48be) @@ -98,26 +54,26 @@ of **agama-securitykey** in action: # License -This project is licensed under the [Apache 2.0](https://github.com/GluuFederation/agama-security-key/blob/main/LICENSE) +This project is licensed under the [Apache 2.0](https://github.com/GluuFederation/agama-securitykey/blob/main/LICENSE) -[contributors-shield]: https://img.shields.io/github/contributors/GluuFederation/agama-security-key.svg?style=for-the-badge +[contributors-shield]: https://img.shields.io/github/contributors/GluuFederation/agama-securitykey.svg?style=for-the-badge -[contributors-url]: https://github.com/GluuFederation/agama-security-key/graphs/contributors +[contributors-url]: https://github.com/GluuFederation/agama-securitykey/graphs/contributors -[forks-shield]: https://img.shields.io/github/forks/GluuFederation/agama-security-key.svg?style=for-the-badge +[forks-shield]: https://img.shields.io/github/forks/GluuFederation/agama-securitykey.svg?style=for-the-badge [forks-url]: https://github.com/GluuFederation/agama-security-key/network/members -[stars-shield]: https://img.shields.io/github/stars/GluuFederation/agama-security-key?style=for-the-badge +[stars-shield]: https://img.shields.io/github/stars/GluuFederation/agama-securitykey?style=for-the-badge -[stars-url]: https://github.com/GluuFederation/agama-security-key/stargazers +[stars-url]: https://github.com/GluuFederation/agama-securitykey/stargazers -[issues-shield]: https://img.shields.io/github/issues/GluuFederation/agama-security-key.svg?style=for-the-badge +[issues-shield]: https://img.shields.io/github/issues/GluuFederation/agama-securitykey.svg?style=for-the-badge -[issues-url]: https://github.com/GluuFederation/agama-security-key/issues +[issues-url]: https://github.com/GluuFederation/agama-securitykey/issues -[license-shield]: https://img.shields.io/github/license/GluuFederation/agama-security-key.svg?style=for-the-badge +[license-shield]: https://img.shields.io/github/license/GluuFederation/agama-securitykey.svg?style=for-the-badge -[license-url]: https://github.com/GluuFederation/agama-security-key/blob/master/LICENSE +[license-url]: https://github.com/GluuFederation/agama-securitykey/blob/master/LICENSE diff --git a/code/org.gluu.agama.securitykey.fidoAuthn.flow b/code/org.gluu.agama.securitykey.fidoAuthn.flow index ea35b36..cf95097 100644 --- a/code/org.gluu.agama.securitykey.fidoAuthn.flow +++ b/code/org.gluu.agama.securitykey.fidoAuthn.flow @@ -1,12 +1,22 @@ +// This flow is responsible for validation with the security key device Flow org.gluu.agama.securitykey.fidoAuthn Basepath "" Inputs userData withEscape +// Mark startup logs Log "@debug Fido Authn flow started!" -jose = Call org.gluu.agama.securitykey.authn.FidoValidator#new +// Get instance FidoValidator +jose = Call org.gluu.agama.securitykey.authn.FidoValidator#new +// Generates the assertionRequest record to initiate validation assertion = Call jose assertionRequest userData.uid +// "obj" variable declaration to be sent to validation view obj = { name: userData.name, escape: withEscape, assertion: assertion } +// Load fido-authn page and pass "obj" obj = RRF "fido-authn.ftlh" obj When obj.skipped is "" - Finish false + it_obhbo = {success:false, error: "false"} + Finish it_obhbo +// Performs the verification after processing the security device obj = Call jose verify obj.tokenResponse -Finish true \ No newline at end of file +// Ends the flow correctly +it_mdehp = {success:false, error: "true"} +Finish it_mdehp \ No newline at end of file diff --git a/code/org.gluu.agama.securitykey.fidoAuthn.json b/code/org.gluu.agama.securitykey.fidoAuthn.json index dedb604..2bc3590 100644 --- a/code/org.gluu.agama.securitykey.fidoAuthn.json +++ b/code/org.gluu.agama.securitykey.fidoAuthn.json @@ -1,8 +1,33 @@ { "nodes": [ + { + "width": 270, + "height": 68, + "id": "Agama-note-b2a6b60c-094c-4cb6-aa8b-36ef4906592b", + "position": { + "x": 1181, + "y": -50 + }, + "type": "notes", + "data": { + "id": "Agama-note-b2a6b60c-094c-4cb6-aa8b-36ef4906592b", + "type": "Agama-note", + "notes": "If the customer cancels the validation process the flow ends, otherwise the validation flow continues.", + "position": { + "x": 1181, + "y": -50 + } + }, + "selected": true, + "positionAbsolute": { + "x": 1181, + "y": -50 + }, + "dragging": false + }, { "width": 170, - "height": 122, + "height": 124, "id": "finish-2a56b16a-c2c1-41af-ae9b-a13af37a9d0a", "position": { "x": 1685, @@ -34,7 +59,7 @@ "parentId": "Agama-call-Node-1aeb4a1a-0c47-4fe9-8e9d-ce6078806f5b", "name": "", "basepath": "", - "comment": "", + "comment": "Ends the flow correctly", "notes": "", "flowfilename": "", "assignments": [ @@ -79,7 +104,7 @@ }, { "width": 170, - "height": 122, + "height": 124, "id": "Agama-call-Node-1aeb4a1a-0c47-4fe9-8e9d-ce6078806f5b", "position": { "x": 1485, @@ -110,12 +135,14 @@ "parentId": "Agama-when-Node-0a9b8e2a-907c-447a-b594-37a311fc7d2c", "name": "", "basepath": "", - "comment": "", + "comment": "Performs the verification after processing the security device", "notes": "", "flowfilename": "", "assignments": [], "callType": "Call method on instance", - "arguments": "obj.tokenResponse", + "arguments": [ + "obj.tokenResponse" + ], "logMessage": "", "templatePath": "", "maxIteration": "", @@ -149,7 +176,7 @@ }, { "width": 170, - "height": 122, + "height": 124, "id": "finish-de636519-7305-41a5-89ae-c11536a5c7e1", "position": { "x": 1486, @@ -211,7 +238,7 @@ "logLevel": "", "assignCallbackResult": "", "displayName": "Finish false", - "nodeColor": "", + "nodeColor": "#eb999a", "exceptionVariableField": "", "finishMode": "withFailure", "hasExtraData": false, @@ -220,7 +247,7 @@ }, "skake": false }, - "selected": true, + "selected": false, "positionAbsolute": { "x": 1486, "y": 232 @@ -228,11 +255,11 @@ "dragging": false }, { - "width": 210, - "height": 140, + "width": 110, + "height": 60, "id": "Agama-when-Node-0a9b8e2a-907c-447a-b594-37a311fc7d2c", "position": { - "x": 1235, + "x": 1236.5, "y": 81 }, "parentId": "Agama-rrf-Node-3bf36af3-e361-4efa-855b-a2309c79a796", @@ -244,7 +271,7 @@ "whenCondition": "", "inRepeatBlock": false, "position": { - "x": 1235, + "x": 1236.5, "y": 81 }, "agamaData": { @@ -264,7 +291,15 @@ "valueField": "\"\"", "hasComment": true, "hasSuccess": true, - "hasFailure": true + "hasFailure": true, + "conditions": [ + { + "variable": "obj.skipped", + "operator": "is", + "dataValue": "\"\"", + "combinator": "none" + } + ] }, "handles": [ "Agama-when-Node-0a9b8e2a-907c-447a-b594-37a311fc7d2c.FAILURE", @@ -275,13 +310,13 @@ "selected": false, "dragging": false, "positionAbsolute": { - "x": 1235, + "x": 1236.5, "y": 81 } }, { "width": 170, - "height": 122, + "height": 124, "id": "Agama-rrf-Node-3bf36af3-e361-4efa-855b-a2309c79a796", "position": { "x": 1030, @@ -312,7 +347,7 @@ "parentId": "Agama-assignment-Node-7d6402d1-120f-498c-b174-77079338be46", "name": "", "basepath": "", - "comment": "", + "comment": "Load fido-authn page and pass \"obj\"", "notes": "", "flowfilename": "", "assignments": [ @@ -323,7 +358,9 @@ } ], "callType": "Call static method", - "arguments": "obj", + "arguments": [ + "obj" + ], "logMessage": "", "templatePath": "fido-authn.ftlh", "maxIteration": "", @@ -357,7 +394,7 @@ }, { "width": 170, - "height": 122, + "height": 124, "id": "Agama-assignment-Node-7d6402d1-120f-498c-b174-77079338be46", "position": { "x": 830, @@ -388,7 +425,7 @@ "parentId": "Agama-call-Node-76a3dde3-a6d8-402e-b411-d6835539ea6b", "name": "", "basepath": "", - "comment": "", + "comment": "\"obj\" variable declaration to be sent to validation view", "notes": "", "flowfilename": "", "assignments": [ @@ -432,10 +469,10 @@ }, { "width": 170, - "height": 122, + "height": 124, "id": "Agama-call-Node-76a3dde3-a6d8-402e-b411-d6835539ea6b", "position": { - "x": 630, + "x": 629, "y": 27 }, "type": "call", @@ -446,7 +483,7 @@ "whenCondition": "", "inRepeatBlock": false, "position": { - "x": 630, + "x": 629, "y": 27 }, "agamaData": { @@ -463,7 +500,7 @@ "parentId": "Agama-call-Node-1d2f9128-9f25-4431-82d3-131b569ce7e3", "name": "", "basepath": "", - "comment": "", + "comment": "Generates the assertionRequest record to initiate validation", "notes": "", "flowfilename": "", "assignments": [ @@ -502,17 +539,17 @@ "selected": false, "dragging": false, "positionAbsolute": { - "x": 630, + "x": 629, "y": 27 } }, { "width": 170, - "height": 122, + "height": 124, "id": "Agama-call-Node-1d2f9128-9f25-4431-82d3-131b569ce7e3", "position": { "x": 430, - "y": 28 + "y": 27.5 }, "type": "call", "data": { @@ -523,7 +560,7 @@ "inRepeatBlock": false, "position": { "x": 430, - "y": 28 + "y": 27.5 }, "agamaData": { "id": "Agama-call-Node-1d2f9128-9f25-4431-82d3-131b569ce7e3", @@ -539,7 +576,7 @@ "parentId": "Agama-log-Node-33b6b4ce-5a64-42ae-bec5-5d4e945ef211", "name": "", "basepath": "", - "comment": "", + "comment": "Get instance FidoValidator", "notes": "", "flowfilename": "", "assignments": [ @@ -579,12 +616,12 @@ "dragging": false, "positionAbsolute": { "x": 430, - "y": 28 + "y": 27.5 } }, { "width": 170, - "height": 122, + "height": 124, "id": "Agama-log-Node-33b6b4ce-5a64-42ae-bec5-5d4e945ef211", "position": { "x": 230, @@ -615,7 +652,7 @@ "parentId": "Agama-start-Flow-b152daa8-8e5d-4204-9518-3c11143edcfc", "name": "", "basepath": "", - "comment": "", + "comment": "Mark startup logs", "notes": "", "flowfilename": "", "assignments": [ @@ -659,7 +696,7 @@ }, { "width": 170, - "height": 122, + "height": 124, "id": "Agama-start-Flow-b152daa8-8e5d-4204-9518-3c11143edcfc", "type": "start", "sourcePosition": "right", @@ -683,7 +720,7 @@ "inRepeatBlock": false, "name": "", "basepath": "", - "comment": "", + "comment": "This flow is responsible for validation with the security key device", "notes": "", "flowfilename": "", "assignments": [ @@ -716,7 +753,8 @@ "configParams": "{}", "flowname": "org.gluu.agama.securitykey.fidoAuthn", "nodeIcon": "material-symbols:line-start-square", - "inputs": "userData withEscape" + "inputs": "userData withEscape", + "timeout": "" }, "skake": false }, @@ -907,8 +945,8 @@ } ], "viewport": { - "x": -1053, - "y": 23, - "zoom": 1 + "x": 147.32377000284862, + "y": 131.7641905356587, + "zoom": 0.6241652744508059 } } \ No newline at end of file diff --git a/code/org.gluu.agama.securitykey.main.flow b/code/org.gluu.agama.securitykey.main.flow index f63c1d9..80e7223 100644 --- a/code/org.gluu.agama.securitykey.main.flow +++ b/code/org.gluu.agama.securitykey.main.flow @@ -1,14 +1,24 @@ +// Main flow that handles all other flows Flow org.gluu.agama.securitykey.main Basepath "" +// Mark startup logs Log "@debug Main flow started!" +// loginForm variable declaration, which is sent to the main form loginForm = { success: true } -casaApi = Call org.gluu.agama.securitykey.CasaApi#new +// Get instance CasaApi +casaApi = Call org.gluu.agama.securitykey.CasaApi#new +// Repeat a maximum of 3 times Repeat 3 times max + // Load main.ftlh page and pass loginForm creds = RRF "main.ftlh" loginForm + // Retrieves the user's information by username userData = Call org.gluu.agama.securitykey.IdentityProcessor#accountFromUid creds.username + // Mark logs of collected user data Log "@debug UserData %" userData + // Assign loginForm.username = creds.username loginForm.username = creds.username When userData is not null + // Initialization of inum and uid inum = userData.inum uid = userData.uid mfaInfo = Call casaApi getMFAUserInfoByFido2 inum @@ -16,13 +26,20 @@ Repeat 3 times max count = mfaInfo.count Log "@debug User % has % credentials enrolled" inum count When count is 0 - it_aagka = { success: false, error: "Your account has no security key configured." } - Finish it_aagka + it_yhkbt = {success:false, error: "Your account has no security key configured."} + Finish it_yhkbt withEscape = true + fidoAuthn = Trigger org.gluu.agama.securitykey.fidoAuthn userData withEscape - Log "@debug Response FidoAuthn %" fidoAuthn - When fidoAuthn.success is true - Finish uid + Log "@debug Response FidoAuthn % % " fidoAuthn fidoAuthn.success + When fidoAuthn.success is not true + it_ugrry = {success:false, error: "Finished with error"} + Finish it_ugrry + Otherwise + it_ssvhe = {success:true, data: { userId: uid, message: "Authentication sucess! "}} + Finish it_ssvhe + // Set the loginForm success field to false loginForm.success = false -it_sxdfm = { success: false, error: "Login attempt exceeded." } -Finish it_sxdfm \ No newline at end of file +it_talkb = {success:false, error: "Login attempt exceeded."} +Finish it_talkb + diff --git a/code/org.gluu.agama.securitykey.main.json b/code/org.gluu.agama.securitykey.main.json index 998dfbf..0d6496f 100644 --- a/code/org.gluu.agama.securitykey.main.json +++ b/code/org.gluu.agama.securitykey.main.json @@ -2,7 +2,104 @@ "nodes": [ { "width": 170, - "height": 122, + "height": 123, + "id": "finish-d71814e8-9b72-4acc-9bf1-f2c7075c240e", + "position": { + "x": 4093.333333333333, + "y": 180 + }, + "type": "finish", + "data": { + "id": "finish-d71814e8-9b72-4acc-9bf1-f2c7075c240e", + "type": "Agama-finish-Flow", + "parentId": "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018", + "whenCondition": "Otherwise", + "inRepeatBlock": false, + "inIterateBlock": false, + "handles": [ + "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018.FAILURE", + "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018.SUCCESS", + "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018.OTHERWISE" + ], + "position": { + "x": 4093.333333333333, + "y": 180 + }, + "agamaData": { + "id": "finish-d71814e8-9b72-4acc-9bf1-f2c7075c240e", + "parentId": "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018", + "type": "Agama-finish-Flow", + "hasSuccess": false, + "hasFailure": false, + "hasComment": true, + "comment": "", + "flowfilename": "", + "returnVariable": "{ userId: uid, message: \"Authentication sucess! \"}", + "finishMode": "withSuccess", + "nodeIcon": "material-symbols:line-end-square-rounded", + "displayName": "Success" + }, + "skake": false + }, + "selected": true, + "dragging": false, + "positionAbsolute": { + "x": 4093.333333333333, + "y": 180 + } + }, + { + "width": 170, + "height": 123, + "id": "finish-88909e5c-5d4d-428b-b250-a958a1be725e", + "position": { + "x": 4094.333333333333, + "y": 755 + }, + "type": "finish", + "data": { + "id": "finish-88909e5c-5d4d-428b-b250-a958a1be725e", + "type": "Agama-finish-Flow", + "parentId": "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018", + "whenCondition": "WhenTrue", + "inRepeatBlock": false, + "inIterateBlock": false, + "handles": [ + "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018.FAILURE", + "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018.SUCCESS", + "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018.OTHERWISE" + ], + "position": { + "x": 4094.333333333333, + "y": 755 + }, + "agamaData": { + "id": "finish-88909e5c-5d4d-428b-b250-a958a1be725e", + "parentId": "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018", + "type": "Agama-finish-Flow", + "hasSuccess": false, + "hasFailure": false, + "hasComment": true, + "comment": "", + "flowfilename": "", + "returnVariable": "Finished with error", + "finishMode": "withFailure", + "nodeIcon": "material-symbols:line-end-square-rounded", + "displayName": "Error", + "nodeColor": "#fda0a0" + }, + "skake": false + }, + "selected": false, + "dragging": false, + "positionAbsolute": { + "x": 4094.333333333333, + "y": 755 + } + }, + { + "width": 170, + "height": 123, "id": "Agama-assignment-Node-6bff7f73-09f5-47db-855c-dd07c9e35d66", "position": { "x": 1983, @@ -77,7 +174,7 @@ }, { "width": 170, - "height": 122, + "height": 123, "id": "finish-50e8d4d6-ffbe-4f51-a172-73bf524e08fc", "position": { "x": 1117, @@ -148,7 +245,7 @@ }, "skake": false }, - "selected": true, + "selected": false, "dragging": false, "positionAbsolute": { "x": 1117, @@ -156,91 +253,11 @@ } }, { - "width": 170, - "height": 122, - "id": "finish-35163906-b1cf-4a51-aad2-e091c4a86a25", - "position": { - "x": 4080, - "y": 663 - }, - "type": "finish", - "data": { - "id": "finish-35163906-b1cf-4a51-aad2-e091c4a86a25", - "type": "Agama-finish-Flow", - "parentId": "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018", - "whenCondition": "WhenTrue", - "inRepeatBlock": true, - "handles": [ - "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018.FAILURE", - "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018.SUCCESS" - ], - "position": { - "x": 4080, - "y": 663 - }, - "agamaData": { - "id": "finish-35163906-b1cf-4a51-aad2-e091c4a86a25", - "type": "Agama-finish-Flow", - "hasSuccess": false, - "hasFailure": false, - "hasRepeat": false, - "hasComment": true, - "hasNoRepeat": false, - "isTopLevelFlow": true, - "whenCondition": "WhenTrue", - "inRepeatBlock": true, - "parentId": "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018", - "name": "", - "basepath": "", - "comment": "", - "notes": "", - "flowfilename": "", - "assignments": [ - { - "variableTypeCheck": true, - "assignmentExpression": "", - "assignedVariableName": "" - } - ], - "callType": "Call static method", - "arguments": [], - "logMessage": "", - "templatePath": "", - "maxIteration": "", - "idpAuthEndpoint": "", - "variableField": "", - "conditionInputField": "", - "valueField": "", - "returnVariable": "uid", - "javaClassName": "", - "javaMethodName": "", - "javaVariableName": "", - "maxIterationVariableName": "", - "logLevel": "", - "assignCallbackResult": "", - "displayName": "Finish uid", - "nodeColor": "", - "exceptionVariableField": "", - "finishMode": "withFailure", - "hasExtraData": false, - "configParams": "{}", - "nodeIcon": "material-symbols:line-end-square-rounded" - }, - "skake": false - }, - "selected": false, - "dragging": false, - "positionAbsolute": { - "x": 4080, - "y": 663 - } - }, - { - "width": 210, - "height": 140, + "width": 110, + "height": 60, "id": "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018", "position": { - "x": 3888, + "x": 4039.333333333333, "y": 525 }, "parentId": "Agama-log-Node-6c7bf514-0783-4793-997e-4fe611846373", @@ -252,7 +269,7 @@ "whenCondition": "", "inRepeatBlock": true, "position": { - "x": 3888, + "x": 4039.333333333333, "y": 525 }, "agamaData": { @@ -271,7 +288,16 @@ "conditionInputField": "is", "valueField": "true", "hasComment": true, - "hasSuccess": true + "hasSuccess": true, + "conditions": [ + { + "variable": "fidoAuthn.success", + "operator": "is not", + "dataValue": "true", + "combinator": "none" + } + ], + "inRepeatBlock": true }, "handles": [ "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018.FAILURE", @@ -282,16 +308,16 @@ "selected": false, "dragging": false, "positionAbsolute": { - "x": 3888, + "x": 4039.333333333333, "y": 525 } }, { "width": 170, - "height": 122, + "height": 123, "id": "Agama-log-Node-6c7bf514-0783-4793-997e-4fe611846373", "position": { - "x": 3673, + "x": 3823, "y": 475 }, "type": "log", @@ -302,7 +328,7 @@ "whenCondition": "", "inRepeatBlock": true, "position": { - "x": 3673, + "x": 3823, "y": 475 }, "agamaData": { @@ -331,7 +357,7 @@ ], "callType": "Call static method", "arguments": [], - "logMessage": "\"Response FidoAuthn %\" fidoAuthn", + "logMessage": "\"Response FidoAuthn % % \" fidoAuthn fidoAuthn.success", "templatePath": "", "maxIteration": "", "idpAuthEndpoint": "", @@ -357,13 +383,13 @@ "selected": false, "dragging": false, "positionAbsolute": { - "x": 3673, + "x": 3823, "y": 475 } }, { "width": 170, - "height": 122, + "height": 123, "id": "finish-b0730d9c-be75-4311-a96d-e7f8f401e4ff", "position": { "x": 3171, @@ -442,8 +468,8 @@ } }, { - "width": 210, - "height": 140, + "width": 110, + "height": 60, "id": "Agama-when-Node-f506e361-5ab3-499c-bb6b-e0ccc49e5ad1", "position": { "x": 3016, @@ -478,7 +504,15 @@ "valueField": "0", "hasComment": true, "hasSuccess": true, - "hasFailure": true + "hasFailure": true, + "conditions": [ + { + "variable": "count", + "operator": "is", + "dataValue": "0", + "combinator": "none" + } + ] }, "handles": [ "Agama-when-Node-f506e361-5ab3-499c-bb6b-e0ccc49e5ad1.FAILURE", @@ -495,7 +529,7 @@ }, { "width": 170, - "height": 122, + "height": 123, "id": "Agama-log-Node-59c922ed-bca1-43ac-b92d-5961edac11f0", "position": { "x": 2806, @@ -570,7 +604,7 @@ }, { "width": 170, - "height": 122, + "height": 123, "id": "Agama-call-Node-ce503f35-ef74-42d5-bdaa-d0de9618ffc4", "position": { "x": 2219, @@ -648,7 +682,7 @@ }, { "width": 170, - "height": 122, + "height": 123, "id": "Agama-assignment-Node-453d3af0-06ba-496a-9265-bdd2a96cc511", "position": { "x": 2027, @@ -723,7 +757,7 @@ }, { "width": 170, - "height": 122, + "height": 123, "id": "Agama-assignment-Node-c418619f-b914-4b42-b214-d681fae5a48a", "position": { "x": 1836, @@ -797,8 +831,8 @@ "dragging": false }, { - "width": 210, - "height": 140, + "width": 110, + "height": 60, "id": "Agama-when-Node-ebb3dc63-b8f3-4405-b1cc-4c3c541feadc", "position": { "x": 1718, @@ -835,7 +869,15 @@ "hasSuccess": "", "hasFailure": true, "hasNoRepeat": false, - "hasRepeat": false + "hasRepeat": false, + "conditions": [ + { + "variable": "userData", + "operator": "is not", + "dataValue": "null", + "combinator": "none" + } + ] }, "handles": [ "Agama-when-Node-ebb3dc63-b8f3-4405-b1cc-4c3c541feadc.FAILURE", @@ -852,7 +894,7 @@ }, { "width": 170, - "height": 122, + "height": 123, "id": "Agama-call-Node-fb1ad7aa-2953-4c81-8231-e6cb5636a7e0", "position": { "x": 1124, @@ -930,7 +972,7 @@ }, { "width": 170, - "height": 122, + "height": 123, "id": "Agama-rrf-Node-9f6ee7dc-524c-4515-a90c-d7185f48f855", "position": { "x": 925, @@ -1006,7 +1048,7 @@ }, { "width": 170, - "height": 122, + "height": 123, "id": "Agama-repeat-Node-a94063b4-1329-4cc0-a1e5-2da976165946", "position": { "x": 830, @@ -1019,7 +1061,10 @@ "parentId": "Agama-call-Node-5a5db4fa-51fe-4f63-aca2-987a05a4e33c", "whenCondition": "", "inRepeatBlock": false, - "handles": [], + "handles": [ + "Agama-repeat-Node-a94063b4-1329-4cc0-a1e5-2da976165946.InFlow", + "Agama-repeat-Node-a94063b4-1329-4cc0-a1e5-2da976165946.InRepeatBlock" + ], "position": { "x": 830, "y": 28 @@ -1082,7 +1127,7 @@ }, { "width": 170, - "height": 122, + "height": 123, "id": "Agama-call-Node-5a5db4fa-51fe-4f63-aca2-987a05a4e33c", "position": { "x": 630, @@ -1158,7 +1203,7 @@ }, { "width": 170, - "height": 122, + "height": 123, "id": "Agama-assignment-Node-dde74028-8751-449f-a64a-33158ec89db2", "position": { "x": 430, @@ -1233,7 +1278,7 @@ }, { "width": 170, - "height": 122, + "height": 123, "id": "Agama-log-Node-39599a98-0915-479f-94b4-ca00fa143214", "position": { "x": 230, @@ -1308,7 +1353,7 @@ }, { "width": 170, - "height": 122, + "height": 123, "id": "Agama-start-Flow-b152daa8-8e5d-4204-9518-3c11143edcfc", "type": "start", "sourcePosition": "right", @@ -1374,7 +1419,7 @@ }, { "width": 170, - "height": 122, + "height": 123, "id": "Agama-assignment-Node-4ce21e85-93a1-4c4b-9d46-1487f7b20cc4", "position": { "x": 3268, @@ -1449,90 +1494,7 @@ }, { "width": 170, - "height": 122, - "id": "Agama-trigger-Node-118e8cc7-12f5-4c84-88cb-27340124f1c4", - "position": { - "x": 3475, - "y": 474 - }, - "type": "trigger", - "data": { - "id": "Agama-trigger-Node-118e8cc7-12f5-4c84-88cb-27340124f1c4", - "type": "Agama-trigger-Node", - "parentId": "Agama-assignment-Node-4ce21e85-93a1-4c4b-9d46-1487f7b20cc4", - "whenCondition": "", - "inRepeatBlock": "", - "position": { - "x": 3475, - "y": 474 - }, - "agamaData": { - "id": "Agama-trigger-Node-118e8cc7-12f5-4c84-88cb-27340124f1c4", - "type": "Agama-trigger-Node", - "hasSuccess": false, - "hasFailure": false, - "hasRepeat": false, - "hasComment": true, - "hasNoRepeat": false, - "isTopLevelFlow": true, - "whenCondition": "WhenFalse", - "inRepeatBlock": true, - "parentId": "Agama-when-Node-f506e361-5ab3-499c-bb6b-e0ccc49e5ad1", - "name": "", - "basepath": "", - "comment": "", - "notes": "", - "flowfilename": "", - "assignments": [ - { - "variableTypeCheck": true, - "assignmentExpression": "", - "assignedVariableName": "" - } - ], - "callType": "Call static method", - "arguments": [ - "userData", - "withEscape" - ], - "logMessage": "", - "templatePath": "", - "maxIteration": "", - "idpAuthEndpoint": "", - "variableField": "", - "conditionInputField": "", - "valueField": "", - "returnVariable": "", - "javaClassName": "", - "javaMethodName": "", - "javaVariableName": "", - "maxIterationVariableName": "", - "logLevel": "", - "assignCallbackResult": "", - "displayName": "FidoAuthn trigger", - "nodeColor": "", - "exceptionVariableField": "", - "hasExtraData": false, - "configParams": "{}", - "nodeIcon": "fluent-mdl2:trigger-approval", - "flowFileName": "org.gluu.agama.securitykey.fidoAuthn", - "asssignedVariableName": "fidoAuthn" - }, - "skake": false - }, - "selected": false, - "dragging": false, - "positionAbsolute": { - "x": 3475, - "y": 474 - }, - "parentId": "Agama-assignment-Node-4ce21e85-93a1-4c4b-9d46-1487f7b20cc4", - "whenCondition": "", - "inRepeatBlock": "" - }, - { - "width": 170, - "height": 122, + "height": 123, "id": "Agama-log-Node-ced6f3aa-230e-4cb3-a223-457dd23de03c", "position": { "x": 2414, @@ -1607,7 +1569,7 @@ }, { "width": 170, - "height": 122, + "height": 123, "id": "Agama-assignment-Node-f9572780-7965-4ed6-b345-8a835fdafddb", "position": { "x": 2606, @@ -1688,7 +1650,7 @@ }, { "width": 170, - "height": 122, + "height": 123, "id": "Agama-log-Node-b731280f-7322-49ff-b8ea-052cc933a0ba", "position": { "x": 1324, @@ -1763,7 +1725,7 @@ }, { "width": 170, - "height": 122, + "height": 123, "id": "Agama-assignment-Node-5f55bd0a-21ad-4368-8d20-d3073122fa3c", "position": { "x": 1517, @@ -1841,6 +1803,137 @@ "parentId": "Agama-log-Node-b731280f-7322-49ff-b8ea-052cc933a0ba", "whenCondition": "", "inRepeatBlock": "" + }, + { + "width": 170, + "height": 123, + "id": "Agama-log-Node-f67b353d-9197-46bc-96ec-7b6e2ec801a0", + "position": { + "x": 3449.3333333333335, + "y": 302 + }, + "type": "log", + "data": { + "id": "Agama-log-Node-f67b353d-9197-46bc-96ec-7b6e2ec801a0", + "type": "Agama-log-Node", + "parentId": "Agama-assignment-Node-4ce21e85-93a1-4c4b-9d46-1487f7b20cc4", + "whenCondition": "WhenFalse", + "inRepeatBlock": false, + "inIterateBlock": false, + "position": { + "x": 3449.3333333333335, + "y": 302 + }, + "agamaData": { + "id": "Agama-log-Node-f67b353d-9197-46bc-96ec-7b6e2ec801a0", + "parentId": "Agama-assignment-Node-4ce21e85-93a1-4c4b-9d46-1487f7b20cc4", + "type": "Agama-log-Node", + "displayName": "", + "hasComment": true, + "comment": "", + "whenCondition": "WhenFalse", + "inRepeatBlock": false, + "inIterateBlock": false, + "logMessage": "\"Just logging *********************\"", + "logLevel": "trace", + "nodeIcon": "octicon:log-16" + }, + "skake": false + }, + "selected": false, + "positionAbsolute": { + "x": 3449.3333333333335, + "y": 302 + }, + "dragging": false + }, + { + "width": 170, + "height": 123, + "id": "Agama-trigger-Node-118e8cc7-12f5-4c84-88cb-27340124f1c4", + "position": { + "x": 3625, + "y": 474 + }, + "type": "trigger", + "data": { + "id": "Agama-trigger-Node-118e8cc7-12f5-4c84-88cb-27340124f1c4", + "type": "Agama-trigger-Node", + "parentId": "Agama-log-Node-f67b353d-9197-46bc-96ec-7b6e2ec801a0", + "whenCondition": "", + "inRepeatBlock": "", + "position": { + "x": 3625, + "y": 474 + }, + "agamaData": { + "id": "Agama-trigger-Node-118e8cc7-12f5-4c84-88cb-27340124f1c4", + "type": "Agama-trigger-Node", + "hasSuccess": false, + "hasFailure": false, + "hasRepeat": false, + "hasComment": true, + "hasNoRepeat": false, + "isTopLevelFlow": true, + "whenCondition": "WhenFalse", + "inRepeatBlock": true, + "parentId": "Agama-log-Node-f67b353d-9197-46bc-96ec-7b6e2ec801a0", + "name": "", + "basepath": "", + "comment": "", + "notes": "", + "flowfilename": "", + "assignments": [ + { + "variableTypeCheck": true, + "assignmentExpression": "", + "assignedVariableName": "" + } + ], + "callType": "Call static method", + "arguments": [ + "userData", + "withEscape" + ], + "logMessage": "", + "templatePath": "", + "maxIteration": "", + "idpAuthEndpoint": "", + "variableField": "", + "conditionInputField": "", + "valueField": "", + "returnVariable": "", + "javaClassName": "", + "javaMethodName": "", + "javaVariableName": "", + "maxIterationVariableName": "", + "logLevel": "", + "assignCallbackResult": "", + "displayName": "FidoAuthn trigger", + "nodeColor": "", + "exceptionVariableField": "", + "hasExtraData": false, + "configParams": "{}", + "nodeIcon": "fluent-mdl2:trigger-approval", + "flowFileName": "org.gluu.agama.securitykey.fidoAuthn", + "asssignedVariableName": "fidoAuthn", + "position": { + "x": 3625 + } + }, + "skake": false, + "inIterateBlock": "" + }, + "selected": false, + "dragging": false, + "positionAbsolute": { + "x": 3625, + "y": 474 + }, + "parentId": "Agama-log-Node-f67b353d-9197-46bc-96ec-7b6e2ec801a0", + "whenCondition": "", + "inRepeatBlock": "", + "inIterateBlock": "" } ], "edges": [ @@ -2102,24 +2195,6 @@ "fillOpacity": 0.7 } }, - { - "id": "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018-finish-35163906-b1cf-4a51-aad2-e091c4a86a25-247554dc-f9e3-4729-9fd7-63854d9fb6b1", - "type": "straight", - "source": "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018", - "target": "finish-35163906-b1cf-4a51-aad2-e091c4a86a25", - "label": "Condition met", - "labelBgPadding": [ - 8, - 4 - ], - "labelBgBorderRadius": 4, - "labelBgStyle": { - "fill": "#FFCC00", - "color": "#fff", - "fillOpacity": 0.7 - }, - "sourceHandle": "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018.SUCCESS" - }, { "id": "Agama-repeat-Node-a94063b4-1329-4cc0-a1e5-2da976165946-finish-50e8d4d6-ffbe-4f51-a172-73bf524e08fc-ea0921a6-8cf6-4936-856a-137d683f652b", "type": "straight", @@ -2156,23 +2231,6 @@ }, "sourceHandle": "Agama-when-Node-f506e361-5ab3-499c-bb6b-e0ccc49e5ad1.FAILURE" }, - { - "id": "Agama-assignment-Node-4ce21e85-93a1-4c4b-9d46-1487f7b20cc4-Agama-trigger-Node-118e8cc7-12f5-4c84-88cb-27340124f1c4-217477c6-767b-4ea7-bb92-cd74edb5d978", - "type": "straight", - "source": "Agama-assignment-Node-4ce21e85-93a1-4c4b-9d46-1487f7b20cc4", - "target": "Agama-trigger-Node-118e8cc7-12f5-4c84-88cb-27340124f1c4", - "label": "", - "labelBgPadding": [ - 8, - 4 - ], - "labelBgBorderRadius": 4, - "labelBgStyle": { - "fill": "#FFCC00", - "color": "#fff", - "fillOpacity": 0.7 - } - }, { "id": "Agama-call-Node-ce503f35-ef74-42d5-bdaa-d0de9618ffc4-Agama-log-Node-ced6f3aa-230e-4cb3-a223-457dd23de03c-b6291f0a-efb2-48e5-b1d9-2b9b697b1ac4", "type": "straight", @@ -2258,11 +2316,81 @@ "fillOpacity": 0.7 }, "sourceHandle": "Agama-when-Node-ebb3dc63-b8f3-4405-b1cc-4c3c541feadc.FAILURE" + }, + { + "id": "Agama-assignment-Node-4ce21e85-93a1-4c4b-9d46-1487f7b20cc4-Agama-log-Node-f67b353d-9197-46bc-96ec-7b6e2ec801a0-37f1a8d6-01e3-45a6-a7b3-2cf9c34f33b8", + "type": "default", + "source": "Agama-assignment-Node-4ce21e85-93a1-4c4b-9d46-1487f7b20cc4", + "target": "Agama-log-Node-f67b353d-9197-46bc-96ec-7b6e2ec801a0", + "label": "", + "labelBgPadding": [ + 8, + 4 + ], + "labelBgBorderRadius": 4, + "labelBgStyle": { + "fill": "#bee0d2", + "color": "#ffffff", + "fillOpacity": 0.7 + } + }, + { + "id": "Agama-log-Node-f67b353d-9197-46bc-96ec-7b6e2ec801a0-Agama-trigger-Node-118e8cc7-12f5-4c84-88cb-27340124f1c4-79be92ea-81ca-42af-bc4d-f14a26114b2b", + "type": "default", + "source": "Agama-log-Node-f67b353d-9197-46bc-96ec-7b6e2ec801a0", + "target": "Agama-trigger-Node-118e8cc7-12f5-4c84-88cb-27340124f1c4", + "label": "", + "labelBgPadding": [ + 8, + 4 + ], + "labelBgBorderRadius": 4, + "labelBgStyle": { + "fill": "#bee0d2", + "color": "#ffffff", + "fillOpacity": 0.7 + } + }, + { + "id": "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018-finish-88909e5c-5d4d-428b-b250-a958a1be725e-c45bcfbf-fc8d-4ded-9dcb-678d93f4139f", + "type": "default", + "source": "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018", + "target": "finish-88909e5c-5d4d-428b-b250-a958a1be725e", + "label": "Condition met", + "labelBgPadding": [ + 8, + 4 + ], + "labelBgBorderRadius": 4, + "labelBgStyle": { + "fill": "#bee0d2", + "color": "#ffffff", + "fillOpacity": 0.7 + }, + "sourceHandle": "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018.SUCCESS" + }, + { + "id": "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018-finish-d71814e8-9b72-4acc-9bf1-f2c7075c240e-c96dd3af-f257-4b13-a5a6-ffa36f90584b", + "type": "default", + "source": "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018", + "target": "finish-d71814e8-9b72-4acc-9bf1-f2c7075c240e", + "label": "Otherwise", + "labelBgPadding": [ + 8, + 4 + ], + "labelBgBorderRadius": 4, + "labelBgStyle": { + "fill": "#bee0d2", + "color": "#ffffff", + "fillOpacity": 0.7 + }, + "sourceHandle": "Agama-when-Node-d871f2c0-b007-42bf-8859-d8cd34bc5018.OTHERWISE" } ], "viewport": { - "x": 10, - "y": 15, - "zoom": 1 + "x": -2080, + "y": -151, + "zoom": 0.75 } } \ No newline at end of file diff --git a/lib/org/gluu/agama/securitykey/CasaWSBase.java b/lib/org/gluu/agama/securitykey/CasaWSBase.java index b2f564a..28b03aa 100644 --- a/lib/org/gluu/agama/securitykey/CasaWSBase.java +++ b/lib/org/gluu/agama/securitykey/CasaWSBase.java @@ -7,7 +7,6 @@ import io.jans.casa.model.ApplicationConfiguration; import io.jans.orm.PersistenceEntryManager; import io.jans.service.cdi.util.CdiUtil; -import io.jans.util.NetworkUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/lib/org/gluu/agama/securitykey/IdentityProcessor.java b/lib/org/gluu/agama/securitykey/IdentityProcessor.java index 1215145..f332769 100644 --- a/lib/org/gluu/agama/securitykey/IdentityProcessor.java +++ b/lib/org/gluu/agama/securitykey/IdentityProcessor.java @@ -12,13 +12,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static io.jans.inbound.Attrs.*; - public class IdentityProcessor { private static final Logger logger = LoggerFactory.getLogger(IdentityProcessor.class); private static final String INUM_ATTR = "inum"; + private static final String UID = "uid"; + private static final String GIVEN_NAME = "givenName"; + private static final String DISPLAY_NAME = "displayName"; + private static final String MAIL = "mail"; public static Map accountFromUid(String uid) { User user = getUser(UID, uid); diff --git a/lib/org/gluu/agama/securitykey/NetworkUtils.java b/lib/org/gluu/agama/securitykey/NetworkUtils.java new file mode 100644 index 0000000..a439450 --- /dev/null +++ b/lib/org/gluu/agama/securitykey/NetworkUtils.java @@ -0,0 +1,12 @@ +package org.gluu.agama.securitykey; + +import io.jans.service.cdi.util.CdiUtil; +import jakarta.servlet.http.HttpServletRequest; + +public class NetworkUtils { + + public static String urlBeforeContextPath() { + HttpServletRequest req = CdiUtil.bean(HttpServletRequest.class); + return req.getScheme() + "://" + req.getServerName(); + } +} diff --git a/lib/org/gluu/agama/securitykey/authn/FidoValidator.java b/lib/org/gluu/agama/securitykey/authn/FidoValidator.java index ff7e374..46d2a8f 100644 --- a/lib/org/gluu/agama/securitykey/authn/FidoValidator.java +++ b/lib/org/gluu/agama/securitykey/authn/FidoValidator.java @@ -2,7 +2,6 @@ import io.jans.fido2.client.AssertionService; import io.jans.fido2.client.Fido2ClientFactory; -import io.jans.util.NetworkUtils; import jakarta.ws.rs.core.Response; @@ -11,6 +10,7 @@ import net.minidev.json.JSONObject; +import org.gluu.agama.securitykey.NetworkUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/project.json b/project.json index 8654b62..e0b5db1 100644 --- a/project.json +++ b/project.json @@ -3,7 +3,7 @@ "description": "Agama SecurityKey", "type": "community", "author": "Milton-Ch", - "version": "0.0.2", + "version": "1.0", "authorWebsite": "https://github.com/GluuFederation/agama-securitykey", "githubUri": "https://github.com/GluuFederation/agama-securitykey", "license": "apache-2.0", @@ -18,7 +18,8 @@ "noDirectLaunch": [], "configs": { "org.gluu.agama.securitykey.fidoAuthn": {}, - "org.gluu.agama.securitykey.main": {} + "org.gluu.agama.securitykey.main": {}, + "": {} }, "name": "agama-securitykey" } \ No newline at end of file diff --git a/web/fido-authn.ftlh b/web/fido-authn.ftlh index ba0f92c..4cdfaf6 100644 --- a/web/fido-authn.ftlh +++ b/web/fido-authn.ftlh @@ -1,25 +1,59 @@ - - - - - - - - - - - - - -
-
- -
- -
-
-

Insert and activate your security key

-
- -
- - -
- Use security key -
-
- -
-
-
-
-
-
-
- - - + \ No newline at end of file diff --git a/web/main.ftlh b/web/main.ftlh index ca69209..d828728 100644 --- a/web/main.ftlh +++ b/web/main.ftlh @@ -1,48 +1,36 @@ - - - - - - - - +[#ftl output_format="HTML"] + + + - + + + + + + + + +
- +

Welcome to Agama SecurityKey

-