@multiplatform.one/wallet 7.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/README.md +8 -0
  2. package/dist/cjs/SiweKeycloakProvider.cjs +273 -0
  3. package/dist/cjs/SiweKeycloakProvider.native.js +309 -0
  4. package/dist/cjs/SiweKeycloakProvider.native.js.map +1 -0
  5. package/dist/cjs/base64.cjs +51 -0
  6. package/dist/cjs/base64.native.js +55 -0
  7. package/dist/cjs/base64.native.js.map +1 -0
  8. package/dist/cjs/base64.spec.cjs +57 -0
  9. package/dist/cjs/base64.spec.native.js +79 -0
  10. package/dist/cjs/base64.spec.native.js.map +1 -0
  11. package/dist/cjs/context.cjs +29 -0
  12. package/dist/cjs/context.native.js +32 -0
  13. package/dist/cjs/context.native.js.map +1 -0
  14. package/dist/cjs/hooks/context.cjs +29 -0
  15. package/dist/cjs/hooks/context.native.js +32 -0
  16. package/dist/cjs/hooks/context.native.js.map +1 -0
  17. package/dist/cjs/hooks/index.cjs +20 -0
  18. package/dist/cjs/hooks/index.native.js +23 -0
  19. package/dist/cjs/hooks/index.native.js.map +1 -0
  20. package/dist/cjs/hooks/useSiweKeycloak.cjs +61 -0
  21. package/dist/cjs/hooks/useSiweKeycloak.native.js +64 -0
  22. package/dist/cjs/hooks/useSiweKeycloak.native.js.map +1 -0
  23. package/dist/cjs/index.cjs +133 -0
  24. package/dist/cjs/index.native.js +189 -0
  25. package/dist/cjs/index.native.js.map +1 -0
  26. package/dist/cjs/render.cjs +78 -0
  27. package/dist/cjs/render.native.js +82 -0
  28. package/dist/cjs/render.native.js.map +1 -0
  29. package/dist/cjs/siwe.cjs +76 -0
  30. package/dist/cjs/siwe.native.js +94 -0
  31. package/dist/cjs/siwe.native.js.map +1 -0
  32. package/dist/cjs/types.cjs +18 -0
  33. package/dist/cjs/types.native.js +21 -0
  34. package/dist/cjs/types.native.js.map +1 -0
  35. package/dist/cjs/util.cjs +53 -0
  36. package/dist/cjs/util.native.js +56 -0
  37. package/dist/cjs/util.native.js.map +1 -0
  38. package/dist/cjs/util.spec.cjs +53 -0
  39. package/dist/cjs/util.spec.native.js +62 -0
  40. package/dist/cjs/util.spec.native.js.map +1 -0
  41. package/dist/esm/SiweKeycloakProvider.mjs +237 -0
  42. package/dist/esm/SiweKeycloakProvider.mjs.map +1 -0
  43. package/dist/esm/SiweKeycloakProvider.native.js +270 -0
  44. package/dist/esm/SiweKeycloakProvider.native.js.map +1 -0
  45. package/dist/esm/base64.mjs +26 -0
  46. package/dist/esm/base64.mjs.map +1 -0
  47. package/dist/esm/base64.native.js +27 -0
  48. package/dist/esm/base64.native.js.map +1 -0
  49. package/dist/esm/base64.spec.mjs +58 -0
  50. package/dist/esm/base64.spec.mjs.map +1 -0
  51. package/dist/esm/base64.spec.native.js +77 -0
  52. package/dist/esm/base64.spec.native.js.map +1 -0
  53. package/dist/esm/context.mjs +4 -0
  54. package/dist/esm/context.mjs.map +1 -0
  55. package/dist/esm/context.native.js +4 -0
  56. package/dist/esm/context.native.js.map +1 -0
  57. package/dist/esm/hooks/context.mjs +4 -0
  58. package/dist/esm/hooks/context.mjs.map +1 -0
  59. package/dist/esm/hooks/context.native.js +4 -0
  60. package/dist/esm/hooks/context.native.js.map +1 -0
  61. package/dist/esm/hooks/index.mjs +2 -0
  62. package/dist/esm/hooks/index.mjs.map +1 -0
  63. package/dist/esm/hooks/index.native.js +2 -0
  64. package/dist/esm/hooks/index.native.js.map +1 -0
  65. package/dist/esm/hooks/useSiweKeycloak.mjs +36 -0
  66. package/dist/esm/hooks/useSiweKeycloak.mjs.map +1 -0
  67. package/dist/esm/hooks/useSiweKeycloak.native.js +36 -0
  68. package/dist/esm/hooks/useSiweKeycloak.native.js.map +1 -0
  69. package/dist/esm/index.mjs +107 -0
  70. package/dist/esm/index.mjs.map +1 -0
  71. package/dist/esm/index.native.js +160 -0
  72. package/dist/esm/index.native.js.map +1 -0
  73. package/dist/esm/render.mjs +42 -0
  74. package/dist/esm/render.mjs.map +1 -0
  75. package/dist/esm/render.native.js +43 -0
  76. package/dist/esm/render.native.js.map +1 -0
  77. package/dist/esm/siwe.mjs +51 -0
  78. package/dist/esm/siwe.mjs.map +1 -0
  79. package/dist/esm/siwe.native.js +66 -0
  80. package/dist/esm/siwe.native.js.map +1 -0
  81. package/dist/esm/types.mjs +2 -0
  82. package/dist/esm/types.mjs.map +1 -0
  83. package/dist/esm/types.native.js +2 -0
  84. package/dist/esm/types.native.js.map +1 -0
  85. package/dist/esm/util.mjs +27 -0
  86. package/dist/esm/util.mjs.map +1 -0
  87. package/dist/esm/util.native.js +27 -0
  88. package/dist/esm/util.native.js.map +1 -0
  89. package/dist/esm/util.spec.mjs +54 -0
  90. package/dist/esm/util.spec.mjs.map +1 -0
  91. package/dist/esm/util.spec.native.js +60 -0
  92. package/dist/esm/util.spec.native.js.map +1 -0
  93. package/package.json +81 -0
  94. package/src/SiweKeycloakProvider.tsx +297 -0
  95. package/src/base64.spec.ts +65 -0
  96. package/src/base64.ts +26 -0
  97. package/src/context.ts +10 -0
  98. package/src/hooks/context.ts +10 -0
  99. package/src/hooks/index.ts +1 -0
  100. package/src/hooks/useSiweKeycloak.ts +23 -0
  101. package/src/index.ts +127 -0
  102. package/src/main/java/com/bitspur/keycloak/siwe/Errors.java +21 -0
  103. package/src/main/java/com/bitspur/keycloak/siwe/LiteAuthenticationService.java +167 -0
  104. package/src/main/java/com/bitspur/keycloak/siwe/SiweCredentials.java +36 -0
  105. package/src/main/java/com/bitspur/keycloak/siwe/SiweSession.java +36 -0
  106. package/src/main/java/com/bitspur/keycloak/siwe/SiweUtil.java +599 -0
  107. package/src/main/java/com/bitspur/keycloak/siwe/Token.java +85 -0
  108. package/src/main/java/com/bitspur/keycloak/siwe/authenticators/browser/SiweBrowserAuthenticator.java +109 -0
  109. package/src/main/java/com/bitspur/keycloak/siwe/authenticators/browser/SiweBrowserAuthenticatorFactory.java +93 -0
  110. package/src/main/java/com/bitspur/keycloak/siwe/authenticators/directgrant/SiweDirectGrantAuthenticator.java +153 -0
  111. package/src/main/java/com/bitspur/keycloak/siwe/authenticators/directgrant/SiweDirectGrantAuthenticatorFactory.java +93 -0
  112. package/src/main/java/com/bitspur/keycloak/siwe/jpa/nonce/SiweNonce.java +95 -0
  113. package/src/main/java/com/bitspur/keycloak/siwe/jpa/nonce/SiweNonceJpaEntityProvider.java +82 -0
  114. package/src/main/java/com/bitspur/keycloak/siwe/jpa/nonce/SiweNonceJpaEntityProviderFactory.java +46 -0
  115. package/src/main/java/com/bitspur/keycloak/siwe/jpa/wallet/SiweWalletAddress.java +103 -0
  116. package/src/main/java/com/bitspur/keycloak/siwe/jpa/wallet/SiweWalletJpaEntityProvider.java +43 -0
  117. package/src/main/java/com/bitspur/keycloak/siwe/jpa/wallet/SiweWalletJpaEntityProviderFactory.java +46 -0
  118. package/src/main/java/com/bitspur/keycloak/siwe/protocol/SiweWalletProtocolMapper.java +85 -0
  119. package/src/main/java/com/bitspur/keycloak/siwe/resource/SiweResource.java +210 -0
  120. package/src/main/java/com/bitspur/keycloak/siwe/resource/SiweResourceProvider.java +34 -0
  121. package/src/main/java/com/bitspur/keycloak/siwe/resource/SiweResourceProviderFactory.java +46 -0
  122. package/src/main/java/com/bitspur/keycloak/siwe/siu/SiuCredentials.java +52 -0
  123. package/src/main/java/com/bitspur/keycloak/siwe/siu/SiuMessage.java +362 -0
  124. package/src/main/java/com/bitspur/keycloak/siwe/siu/SiuParseException.java +23 -0
  125. package/src/main/java/com/bitspur/keycloak/siwe/siu/SiuUtil.java +547 -0
  126. package/src/main/java/com/bitspur/keycloak/siwe/siu/authenticators/browser/SiuBrowserAuthenticator.java +112 -0
  127. package/src/main/java/com/bitspur/keycloak/siwe/siu/authenticators/browser/SiuBrowserAuthenticatorFactory.java +96 -0
  128. package/src/main/java/com/bitspur/keycloak/siwe/siu/authenticators/directgrant/SiuDirectGrantAuthenticator.java +169 -0
  129. package/src/main/java/com/bitspur/keycloak/siwe/siu/authenticators/directgrant/SiuDirectGrantAuthenticatorFactory.java +96 -0
  130. package/src/main/java/com/bitspur/keycloak/siwe/siu/resource/SiuResource.java +244 -0
  131. package/src/main/java/com/bitspur/keycloak/siwe/siu/resource/SiuResourceProvider.java +34 -0
  132. package/src/main/java/com/bitspur/keycloak/siwe/siu/resource/SiuResourceProviderFactory.java +48 -0
  133. package/src/main/java/com/bitspur/keycloak/siwe/siws/SiwsCredentials.java +52 -0
  134. package/src/main/java/com/bitspur/keycloak/siwe/siws/SiwsMessage.java +362 -0
  135. package/src/main/java/com/bitspur/keycloak/siwe/siws/SiwsParseException.java +23 -0
  136. package/src/main/java/com/bitspur/keycloak/siwe/siws/SiwsUtil.java +564 -0
  137. package/src/main/java/com/bitspur/keycloak/siwe/siws/authenticators/browser/SiwsBrowserAuthenticator.java +112 -0
  138. package/src/main/java/com/bitspur/keycloak/siwe/siws/authenticators/browser/SiwsBrowserAuthenticatorFactory.java +96 -0
  139. package/src/main/java/com/bitspur/keycloak/siwe/siws/authenticators/directgrant/SiwsDirectGrantAuthenticator.java +169 -0
  140. package/src/main/java/com/bitspur/keycloak/siwe/siws/authenticators/directgrant/SiwsDirectGrantAuthenticatorFactory.java +96 -0
  141. package/src/main/java/com/bitspur/keycloak/siwe/siws/resource/SiwsResource.java +244 -0
  142. package/src/main/java/com/bitspur/keycloak/siwe/siws/resource/SiwsResourceProvider.java +34 -0
  143. package/src/main/java/com/bitspur/keycloak/siwe/siws/resource/SiwsResourceProviderFactory.java +48 -0
  144. package/src/main/resources/META-INF/keycloak-themes.json +8 -0
  145. package/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory +6 -0
  146. package/src/main/resources/META-INF/services/org.keycloak.connections.jpa.entityprovider.JpaEntityProviderFactory +2 -0
  147. package/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper +1 -0
  148. package/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory +3 -0
  149. package/src/main/resources/META-INF/siwe-nonce-changelog.xml +29 -0
  150. package/src/main/resources/META-INF/siwe-wallet-changelog.xml +46 -0
  151. package/src/render.tsx +47 -0
  152. package/src/siwe.tsx +57 -0
  153. package/src/types.ts +6 -0
  154. package/src/util.spec.ts +72 -0
  155. package/src/util.ts +28 -0
@@ -0,0 +1,103 @@
1
+ /**
2
+ * File: /src/main/java/com/bitspur/keycloak/siwe/jpa/wallet/SiweWalletAddress.java
3
+ * Project: siwe-keycloak
4
+ * File Created: 13-03-2024
5
+ * Author: Clay Risser
6
+ * -----
7
+ * BitSpur (c) Copyright 2022 - 2024
8
+ */
9
+
10
+ package com.bitspur.keycloak.siwe.jpa.wallet;
11
+
12
+ import java.time.LocalDateTime;
13
+
14
+ import jakarta.persistence.Column;
15
+ import jakarta.persistence.Entity;
16
+ import jakarta.persistence.GeneratedValue;
17
+ import jakarta.persistence.GenerationType;
18
+ import jakarta.persistence.Id;
19
+ import jakarta.persistence.NamedQueries;
20
+ import jakarta.persistence.NamedQuery;
21
+ import jakarta.persistence.Table;
22
+
23
+ import org.hibernate.annotations.CreationTimestamp;
24
+ import org.hibernate.annotations.UpdateTimestamp;
25
+
26
+ @Entity
27
+ @Table(name = "SIWE_WALLET_ADDRESS")
28
+ @NamedQueries({
29
+ @NamedQuery(name = "findByAddress", query = "from SiweWalletAddress where address = :address"),
30
+ @NamedQuery(name = "findByUserId", query = "from SiweWalletAddress where userId = :userId"),
31
+ @NamedQuery(name = "findByRealmAddress", query = "from SiweWalletAddress where realmId = :realmId and address = :address"),
32
+ @NamedQuery(name = "findByRealmAddressType", query = "from SiweWalletAddress where realmId = :realmId and address = :address and addressType = :addressType")
33
+ })
34
+ public class SiweWalletAddress {
35
+ @Id
36
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
37
+ @Column(name = "ID", nullable = false)
38
+ private Long id;
39
+
40
+ @Column(name = "ADDRESS", nullable = false)
41
+ private String address;
42
+
43
+ @Column(name = "ADDRESS_TYPE", nullable = false)
44
+ private String addressType;
45
+
46
+ @Column(name = "USER_ID", nullable = false)
47
+ private String userId;
48
+
49
+ @Column(name = "REALM_ID", nullable = false)
50
+ private String realmId;
51
+
52
+ @CreationTimestamp
53
+ @Column(name = "CREATED_AT", nullable = false, updatable = false)
54
+ private LocalDateTime createdAt;
55
+
56
+ @UpdateTimestamp
57
+ @Column(name = "UPDATED_AT", nullable = false)
58
+ private LocalDateTime updatedAt;
59
+
60
+ public Long getId() {
61
+ return this.id;
62
+ }
63
+
64
+ public String getAddress() {
65
+ return this.address;
66
+ }
67
+
68
+ public String getAddressType() {
69
+ return this.addressType;
70
+ }
71
+
72
+ public String getUserId() {
73
+ return this.userId;
74
+ }
75
+
76
+ public String getRealmId() {
77
+ return this.realmId;
78
+ }
79
+
80
+ public LocalDateTime getCreatedAt() {
81
+ return this.createdAt;
82
+ }
83
+
84
+ public LocalDateTime getUpdatedAt() {
85
+ return this.updatedAt;
86
+ }
87
+
88
+ public void setAddress(String address) {
89
+ this.address = address;
90
+ }
91
+
92
+ public void setAddressType(String addressType) {
93
+ this.addressType = addressType;
94
+ }
95
+
96
+ public void setUserId(String userId) {
97
+ this.userId = userId;
98
+ }
99
+
100
+ public void setRealmId(String realmId) {
101
+ this.realmId = realmId;
102
+ }
103
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * File: /src/main/java/com/bitspur/keycloak/siwe/jpa/wallet/SiweWalletJpaEntityProvider.java
3
+ * Project: siwe-keycloak
4
+ * File Created: 13-03-2024
5
+ * Author: Clay Risser
6
+ * -----
7
+ * BitSpur (c) Copyright 2022 - 2024
8
+ */
9
+
10
+ package com.bitspur.keycloak.siwe.jpa.wallet;
11
+
12
+ import org.keycloak.connections.jpa.entityprovider.JpaEntityProvider;
13
+ import org.keycloak.models.KeycloakSession;
14
+
15
+ import java.util.Collections;
16
+ import java.util.List;
17
+
18
+ public class SiweWalletJpaEntityProvider implements JpaEntityProvider {
19
+ KeycloakSession session;
20
+
21
+ SiweWalletJpaEntityProvider(KeycloakSession session) {
22
+ this.session = session;
23
+ }
24
+
25
+ @Override
26
+ public List<Class<?>> getEntities() {
27
+ return Collections.<Class<?>>singletonList(SiweWalletAddress.class);
28
+ }
29
+
30
+ @Override
31
+ public String getChangelogLocation() {
32
+ return "META-INF/siwe-wallet-changelog.xml";
33
+ }
34
+
35
+ @Override
36
+ public void close() {
37
+ }
38
+
39
+ @Override
40
+ public String getFactoryId() {
41
+ return SiweWalletJpaEntityProviderFactory.ID;
42
+ }
43
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * File: /src/main/java/com/bitspur/keycloak/siwe/jpa/wallet/SiweWalletJpaEntityProviderFactory.java
3
+ * Project: siwe-keycloak
4
+ * File Created: 20-01-2025 07:52:54
5
+ * Author: Clay Risser
6
+ * -----
7
+ * Last Modified: 20-01-2025 08:04:48
8
+ * Modified By: Clay Risser
9
+ * -----
10
+ * BitSpur (c) Copyright 2022 - 2025
11
+ */
12
+
13
+ package com.bitspur.keycloak.siwe.jpa.wallet;
14
+
15
+ import org.keycloak.Config.Scope;
16
+ import org.keycloak.connections.jpa.entityprovider.JpaEntityProvider;
17
+ import org.keycloak.connections.jpa.entityprovider.JpaEntityProviderFactory;
18
+ import org.keycloak.models.KeycloakSession;
19
+ import org.keycloak.models.KeycloakSessionFactory;
20
+
21
+ public class SiweWalletJpaEntityProviderFactory implements JpaEntityProviderFactory {
22
+
23
+ protected static final String ID = "siwe-wallet-entity-provider";
24
+
25
+ @Override
26
+ public JpaEntityProvider create(KeycloakSession session) {
27
+ return new SiweWalletJpaEntityProvider(session);
28
+ }
29
+
30
+ @Override
31
+ public String getId() {
32
+ return ID;
33
+ }
34
+
35
+ @Override
36
+ public void init(Scope config) {
37
+ }
38
+
39
+ @Override
40
+ public void postInit(KeycloakSessionFactory factory) {
41
+ }
42
+
43
+ @Override
44
+ public void close() {
45
+ }
46
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * File: /src/main/java/com/bitspur/keycloak/siwe/protocol/SiweWalletProtocolMapper.java
3
+ * Project: siwe-keycloak
4
+ * File Created: 20-01-2025 16:31:23
5
+ * Author: Clay Risser
6
+ * -----
7
+ * Last Modified: 20-01-2025 16:47:30
8
+ * Modified By: Clay Risser
9
+ * -----
10
+ * BitSpur (c) Copyright 2022 - 2025
11
+ */
12
+
13
+ package com.bitspur.keycloak.siwe.protocol;
14
+
15
+ import com.bitspur.keycloak.siwe.SiweUtil;
16
+
17
+ import org.keycloak.models.ClientSessionContext;
18
+ import org.keycloak.models.KeycloakSession;
19
+ import org.keycloak.models.ProtocolMapperModel;
20
+ import org.keycloak.models.UserSessionModel;
21
+ import org.keycloak.protocol.oidc.mappers.AbstractOIDCProtocolMapper;
22
+ import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper;
23
+ import org.keycloak.protocol.oidc.mappers.OIDCIDTokenMapper;
24
+ import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
25
+ import org.keycloak.protocol.oidc.mappers.UserInfoTokenMapper;
26
+ import org.keycloak.provider.ProviderConfigProperty;
27
+ import org.keycloak.representations.IDToken;
28
+
29
+ import java.util.ArrayList;
30
+ import java.util.List;
31
+
32
+ public class SiweWalletProtocolMapper extends AbstractOIDCProtocolMapper
33
+ implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {
34
+
35
+ public static final String PROVIDER_ID = "siwe-wallet-mapper";
36
+ public static final String CLAIM_NAME = "wallets";
37
+
38
+ private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
39
+
40
+ static {
41
+ ProviderConfigProperty property = new ProviderConfigProperty();
42
+ property.setName(CLAIM_NAME);
43
+ property.setLabel("Wallet Addresses Claim Name");
44
+ property.setType(ProviderConfigProperty.STRING_TYPE);
45
+ property.setDefaultValue(CLAIM_NAME);
46
+ property.setHelpText("Name of the claim to add to the token");
47
+ configProperties.add(property);
48
+ OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, SiweWalletProtocolMapper.class);
49
+ }
50
+
51
+ @Override
52
+ public String getDisplayCategory() {
53
+ return "Token mapper";
54
+ }
55
+
56
+ @Override
57
+ public String getDisplayType() {
58
+ return "SIWE Wallet Addresses";
59
+ }
60
+
61
+ @Override
62
+ public String getHelpText() {
63
+ return "Adds wallet addresses to the token in a structured format";
64
+ }
65
+
66
+ @Override
67
+ public List<ProviderConfigProperty> getConfigProperties() {
68
+ return configProperties;
69
+ }
70
+
71
+ @Override
72
+ public String getId() {
73
+ return PROVIDER_ID;
74
+ }
75
+
76
+ @Override
77
+ protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession,
78
+ KeycloakSession keycloakSession, ClientSessionContext clientSessionCtx) {
79
+ String claimName = mappingModel.getConfig().get(CLAIM_NAME);
80
+ if (claimName == null) {
81
+ claimName = CLAIM_NAME;
82
+ }
83
+ SiweUtil.addWalletsToToken(keycloakSession, userSession.getUser(), token.getOtherClaims());
84
+ }
85
+ }
@@ -0,0 +1,210 @@
1
+ /**
2
+ * File: /src/main/java/com/bitspur/keycloak/siwe/resource/SiweResource.java
3
+ * Project: siwe-keycloak
4
+ * File Created: 04-11-2024 22:05:52
5
+ * Author: Clay Risser
6
+ * -----
7
+ * Last Modified: 24-01-2026 00:40:00
8
+ * Modified By: Clay Risser
9
+ * -----
10
+ * BitSpur (c) Copyright 2022 - 2026
11
+ */
12
+
13
+ package com.bitspur.keycloak.siwe.resource;
14
+
15
+ import jakarta.json.Json;
16
+ import jakarta.json.JsonObject;
17
+ import jakarta.ws.rs.GET;
18
+ import jakarta.ws.rs.OPTIONS;
19
+ import jakarta.ws.rs.POST;
20
+ import jakarta.ws.rs.Path;
21
+ import jakarta.ws.rs.Produces;
22
+ import jakarta.ws.rs.core.MediaType;
23
+ import jakarta.ws.rs.core.Response;
24
+
25
+ import org.jboss.logging.Logger;
26
+ import org.keycloak.models.KeycloakSession;
27
+ import org.keycloak.models.UserModel;
28
+ import org.keycloak.services.managers.AuthenticationManager.AuthResult;
29
+
30
+ import com.bitspur.keycloak.siwe.SiweUtil;
31
+ import com.bitspur.keycloak.siwe.LiteAuthenticationService;
32
+ import com.bitspur.keycloak.siwe.SiweSession;
33
+ import com.bitspur.keycloak.siwe.SiweCredentials;
34
+
35
+ import java.io.StringReader;
36
+ import java.util.Base64;
37
+
38
+ public class SiweResource {
39
+ private static final Logger logger = Logger.getLogger(SiweResource.class);
40
+ private final KeycloakSession session;
41
+ private LiteAuthenticationService liteAuthenticationService;
42
+
43
+ public SiweResource(KeycloakSession session) {
44
+ this.session = session;
45
+ this.liteAuthenticationService = new LiteAuthenticationService(this.session);
46
+ }
47
+
48
+ @OPTIONS
49
+ @Path("nonce")
50
+ public Response optionsNonce() {
51
+ return createCorsPreflightResponse();
52
+ }
53
+
54
+ @OPTIONS
55
+ @Path("session")
56
+ public Response optionsSession() {
57
+ return createCorsPreflightResponse();
58
+ }
59
+
60
+ @OPTIONS
61
+ @Path("register")
62
+ public Response optionsRegister() {
63
+ return createCorsPreflightResponse();
64
+ }
65
+
66
+ @OPTIONS
67
+ @Path("unregister")
68
+ public Response optionsUnregister() {
69
+ return createCorsPreflightResponse();
70
+ }
71
+
72
+ @GET
73
+ @Path("nonce")
74
+ @Produces({ MediaType.TEXT_PLAIN })
75
+ public Response getNonce() {
76
+ SiweSession siweSession = SiweUtil.generateSiweSession(this.session);
77
+ // cannot use Cookie and NewCookie because it does not support SameSite=None
78
+ String cookieString = String.format("%s=%s; Path=/; Secure; HttpOnly; SameSite=None",
79
+ SiweUtil.KEYCLOAK_SESSION_SIWE, siweSession.getKey());
80
+ return addCorsHeaders(Response.ok(siweSession.getNonce(), MediaType.TEXT_PLAIN)
81
+ .header("Set-Cookie", cookieString))
82
+ .build();
83
+ }
84
+
85
+ @GET
86
+ @Path("session")
87
+ @Produces({ MediaType.APPLICATION_JSON })
88
+ public Response getSession() {
89
+ UserModel user = this.getCurrentUser();
90
+ if (user == null) {
91
+ return this.createErrorResponse(new Exception("No user"), Response.Status.UNAUTHORIZED);
92
+ }
93
+ JsonObject json = Json.createObjectBuilder()
94
+ .add("address", user.getUsername())
95
+ .add("chainId", "1").build();
96
+ return this.createResponse(json.toString(), Response.Status.OK);
97
+ }
98
+
99
+ private Response createErrorResponse(Exception e, Response.Status status) {
100
+ JsonObject json = Json.createObjectBuilder().add("message", e.getMessage()).build();
101
+ return this.createResponse(json.toString(), status);
102
+ }
103
+
104
+ private Response createResponse(String body, Response.Status status) {
105
+ return this.createResponse(body, status, MediaType.APPLICATION_JSON);
106
+ }
107
+
108
+ private Response createResponse(String body, Response.Status status, String type) {
109
+ return addCorsHeaders(Response.ok(body, type).status(status)).build();
110
+ }
111
+
112
+ private Response.ResponseBuilder addCorsHeaders(Response.ResponseBuilder builder) {
113
+ return builder
114
+ .header("Access-Control-Allow-Origin", "*")
115
+ .header("Access-Control-Allow-Credentials", "true")
116
+ .header("Access-Control-Allow-Headers", "Authorization, Content-Type")
117
+ .header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
118
+ .header("Access-Control-Expose-Headers", "Authorization");
119
+ }
120
+
121
+ private Response createCorsPreflightResponse() {
122
+ return addCorsHeaders(Response.ok()).build();
123
+ }
124
+
125
+ @POST
126
+ @Path("register")
127
+ @Produces(MediaType.APPLICATION_JSON)
128
+ public Response register(String body) {
129
+ UserModel user = this.getCurrentUser();
130
+ if (user == null) {
131
+ return this.createErrorResponse(new Exception("no user"), Response.Status.UNAUTHORIZED);
132
+ }
133
+ try {
134
+ logger.info("Register request body: " + body);
135
+ JsonObject jsonBody = Json.createReader(new StringReader(body)).readObject();
136
+ String message = jsonBody.getString("message");
137
+ String signature = jsonBody.getString("signature");
138
+ logger.info("Parsed message: " + message);
139
+ logger.info("Parsed signature: " + signature);
140
+
141
+ try {
142
+ SiweCredentials siweCredentials = new SiweCredentials(signature,
143
+ Base64.getEncoder().encodeToString(message.getBytes()));
144
+ logger.info("Successfully created SiweCredentials");
145
+ logger.info("Parsed SIWE message details:");
146
+ logger.info("- Domain: " + siweCredentials.message.getDomain());
147
+ logger.info("- Address: " + siweCredentials.message.getAddress());
148
+ logger.info("- URI: " + siweCredentials.message.getUri());
149
+ logger.info("- Version: " + siweCredentials.message.getVersion());
150
+ logger.info("- Chain ID: " + siweCredentials.message.getChainId());
151
+ logger.info("- Nonce: " + siweCredentials.message.getNonce());
152
+ logger.info("- Issued At: " + siweCredentials.message.getIssuedAt());
153
+
154
+ String address = siweCredentials.message.getAddress();
155
+ UserModel existingUser = SiweUtil.findUserByWalletAddress(session, address);
156
+ if (existingUser != null) {
157
+ return this.createErrorResponse(new Exception("wallet already registered"), Response.Status.CONFLICT);
158
+ }
159
+ JsonObject result = Json.createObjectBuilder()
160
+ .add("registered", SiweUtil.addWalletAddress(session, user, address))
161
+ .build();
162
+ return this.createResponse(result.toString(), Response.Status.OK);
163
+ } catch (Exception e) {
164
+ logger.error("SIWE message parsing failed:", e);
165
+ return this.createErrorResponse(new Exception("invalid siwe message: " + e.getMessage()),
166
+ Response.Status.BAD_REQUEST);
167
+ }
168
+ } catch (Exception e) {
169
+ logger.error("Request processing failed:", e);
170
+ return this.createErrorResponse(e, Response.Status.BAD_REQUEST);
171
+ }
172
+ }
173
+
174
+ @POST
175
+ @Path("unregister")
176
+ @Produces(MediaType.APPLICATION_JSON)
177
+ public Response unregister(String body) {
178
+ UserModel user = this.getCurrentUser();
179
+ if (user == null) {
180
+ return this.createErrorResponse(new Exception("no user"), Response.Status.UNAUTHORIZED);
181
+ }
182
+ try {
183
+ JsonObject bodyJson = Json.createReader(new StringReader(body)).readObject();
184
+ String address = bodyJson.getString("address");
185
+ if (address == null || address.isEmpty()) {
186
+ return this.createErrorResponse(new Exception("no address provided"), Response.Status.BAD_REQUEST);
187
+ }
188
+ UserModel existingUser = SiweUtil.findUserByWalletAddress(session, address);
189
+ if (existingUser == null || !existingUser.getId().equals(user.getId())) {
190
+ return this.createErrorResponse(new Exception("wallet not found"), Response.Status.NOT_FOUND);
191
+ }
192
+ JsonObject result = Json.createObjectBuilder()
193
+ .add("unregistered", SiweUtil.removeWalletAddress(session, address))
194
+ .build();
195
+ return this.createResponse(result.toString(), Response.Status.OK);
196
+ } catch (Exception e) {
197
+ return this.createErrorResponse(e, Response.Status.BAD_REQUEST);
198
+ }
199
+ }
200
+
201
+ private UserModel getCurrentUser() {
202
+ AuthResult authResult = this.liteAuthenticationService.resolveAuthentication(
203
+ this.session.getContext().getRequestHeaders(),
204
+ this.session.getContext().getConnection());
205
+ if (authResult == null) {
206
+ return null;
207
+ }
208
+ return authResult.getUser();
209
+ }
210
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * File: /src/main/java/com/bitspur/keycloak/siwe/resource/SiweResourceProvider.java
3
+ * Project: authenticators
4
+ * File Created: 08-04-2023 11:27:18
5
+ * Author: Clay Risser
6
+ * -----
7
+ * Last Modified: 10-08-2023 16:34:52
8
+ * Modified By: Clay Risser
9
+ * -----
10
+ * BitSpur (c) Copyright 2022 - 2023
11
+ */
12
+
13
+ package com.bitspur.keycloak.siwe.resource;
14
+
15
+ import org.keycloak.models.KeycloakSession;
16
+ import org.keycloak.services.resource.RealmResourceProvider;
17
+
18
+ public class SiweResourceProvider implements RealmResourceProvider {
19
+
20
+ private final KeycloakSession session;
21
+
22
+ public SiweResourceProvider(KeycloakSession session) {
23
+ this.session = session;
24
+ }
25
+
26
+ @Override
27
+ public Object getResource() {
28
+ return new SiweResource(this.session);
29
+ }
30
+
31
+ @Override
32
+ public void close() {
33
+ }
34
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * File: /src/main/java/com/bitspur/keycloak/siwe/resource/SiweResourceProviderFactory.java
3
+ * Project: authenticators
4
+ * File Created: 08-04-2023 11:25:27
5
+ * Author: Clay Risser
6
+ * -----
7
+ * Last Modified: 10-08-2023 16:34:52
8
+ * Modified By: Clay Risser
9
+ * -----
10
+ * BitSpur (c) Copyright 2022 - 2023
11
+ */
12
+
13
+ package com.bitspur.keycloak.siwe.resource;
14
+
15
+ import org.keycloak.models.KeycloakSession;
16
+ import org.keycloak.models.KeycloakSessionFactory;
17
+ import org.keycloak.services.resource.RealmResourceProvider;
18
+ import org.keycloak.services.resource.RealmResourceProviderFactory;
19
+ import static org.keycloak.Config.Scope;
20
+
21
+ public class SiweResourceProviderFactory implements RealmResourceProviderFactory {
22
+ public static final String ID = "siwe";
23
+
24
+ @Override
25
+ public RealmResourceProvider create(KeycloakSession session) {
26
+ return new SiweResourceProvider(session);
27
+ }
28
+
29
+ @Override
30
+ public void init(Scope scope) {
31
+ }
32
+
33
+ @Override
34
+ public void postInit(KeycloakSessionFactory keycloakSessionFactory) {
35
+
36
+ }
37
+
38
+ @Override
39
+ public void close() {
40
+ }
41
+
42
+ @Override
43
+ public String getId() {
44
+ return ID;
45
+ }
46
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * File: /src/main/java/com/bitspur/keycloak/siwe/siu/SiuCredentials.java
3
+ * Project: siwe-keycloak
4
+ * File Created: 24-01-2026
5
+ * Author: Clay Risser
6
+ * -----
7
+ * BitSpur (c) Copyright 2022 - 2026
8
+ */
9
+
10
+ package com.bitspur.keycloak.siwe.siu;
11
+
12
+ import com.bitspur.keycloak.siwe.Errors.SiweKeycloakException;
13
+ import java.util.Base64;
14
+
15
+ /**
16
+ * Holds SIU credentials: the parsed message and the signature.
17
+ *
18
+ * The signature is a Base64-encoded ed25519 signature.
19
+ * The message is a Base64-encoded SIU message string.
20
+ */
21
+ public class SiuCredentials {
22
+ private static final Base64.Decoder decoder = Base64.getDecoder();
23
+
24
+ /**
25
+ * The parsed SIU message.
26
+ */
27
+ public SiuMessage message;
28
+
29
+ /**
30
+ * The Base64-encoded ed25519 signature.
31
+ */
32
+ public String signature;
33
+
34
+ /**
35
+ * Creates new SIU credentials.
36
+ *
37
+ * @param username The signature (Base64 encoded)
38
+ * @param password The SIU message (Base64 encoded)
39
+ * @throws SiweKeycloakException If the message cannot be parsed
40
+ */
41
+ public SiuCredentials(String username, String password) throws SiweKeycloakException {
42
+ this.signature = username;
43
+ try {
44
+ String decodedMessage = new String(decoder.decode(password));
45
+ this.message = new SiuMessage.Parser().parse(decodedMessage);
46
+ } catch (SiuParseException e) {
47
+ throw new SiweKeycloakException(e.getMessage());
48
+ } catch (IllegalArgumentException e) {
49
+ throw new SiweKeycloakException("Invalid Base64 encoding for SIU message");
50
+ }
51
+ }
52
+ }