@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.
- package/README.md +8 -0
- package/dist/cjs/SiweKeycloakProvider.cjs +273 -0
- package/dist/cjs/SiweKeycloakProvider.native.js +309 -0
- package/dist/cjs/SiweKeycloakProvider.native.js.map +1 -0
- package/dist/cjs/base64.cjs +51 -0
- package/dist/cjs/base64.native.js +55 -0
- package/dist/cjs/base64.native.js.map +1 -0
- package/dist/cjs/base64.spec.cjs +57 -0
- package/dist/cjs/base64.spec.native.js +79 -0
- package/dist/cjs/base64.spec.native.js.map +1 -0
- package/dist/cjs/context.cjs +29 -0
- package/dist/cjs/context.native.js +32 -0
- package/dist/cjs/context.native.js.map +1 -0
- package/dist/cjs/hooks/context.cjs +29 -0
- package/dist/cjs/hooks/context.native.js +32 -0
- package/dist/cjs/hooks/context.native.js.map +1 -0
- package/dist/cjs/hooks/index.cjs +20 -0
- package/dist/cjs/hooks/index.native.js +23 -0
- package/dist/cjs/hooks/index.native.js.map +1 -0
- package/dist/cjs/hooks/useSiweKeycloak.cjs +61 -0
- package/dist/cjs/hooks/useSiweKeycloak.native.js +64 -0
- package/dist/cjs/hooks/useSiweKeycloak.native.js.map +1 -0
- package/dist/cjs/index.cjs +133 -0
- package/dist/cjs/index.native.js +189 -0
- package/dist/cjs/index.native.js.map +1 -0
- package/dist/cjs/render.cjs +78 -0
- package/dist/cjs/render.native.js +82 -0
- package/dist/cjs/render.native.js.map +1 -0
- package/dist/cjs/siwe.cjs +76 -0
- package/dist/cjs/siwe.native.js +94 -0
- package/dist/cjs/siwe.native.js.map +1 -0
- package/dist/cjs/types.cjs +18 -0
- package/dist/cjs/types.native.js +21 -0
- package/dist/cjs/types.native.js.map +1 -0
- package/dist/cjs/util.cjs +53 -0
- package/dist/cjs/util.native.js +56 -0
- package/dist/cjs/util.native.js.map +1 -0
- package/dist/cjs/util.spec.cjs +53 -0
- package/dist/cjs/util.spec.native.js +62 -0
- package/dist/cjs/util.spec.native.js.map +1 -0
- package/dist/esm/SiweKeycloakProvider.mjs +237 -0
- package/dist/esm/SiweKeycloakProvider.mjs.map +1 -0
- package/dist/esm/SiweKeycloakProvider.native.js +270 -0
- package/dist/esm/SiweKeycloakProvider.native.js.map +1 -0
- package/dist/esm/base64.mjs +26 -0
- package/dist/esm/base64.mjs.map +1 -0
- package/dist/esm/base64.native.js +27 -0
- package/dist/esm/base64.native.js.map +1 -0
- package/dist/esm/base64.spec.mjs +58 -0
- package/dist/esm/base64.spec.mjs.map +1 -0
- package/dist/esm/base64.spec.native.js +77 -0
- package/dist/esm/base64.spec.native.js.map +1 -0
- package/dist/esm/context.mjs +4 -0
- package/dist/esm/context.mjs.map +1 -0
- package/dist/esm/context.native.js +4 -0
- package/dist/esm/context.native.js.map +1 -0
- package/dist/esm/hooks/context.mjs +4 -0
- package/dist/esm/hooks/context.mjs.map +1 -0
- package/dist/esm/hooks/context.native.js +4 -0
- package/dist/esm/hooks/context.native.js.map +1 -0
- package/dist/esm/hooks/index.mjs +2 -0
- package/dist/esm/hooks/index.mjs.map +1 -0
- package/dist/esm/hooks/index.native.js +2 -0
- package/dist/esm/hooks/index.native.js.map +1 -0
- package/dist/esm/hooks/useSiweKeycloak.mjs +36 -0
- package/dist/esm/hooks/useSiweKeycloak.mjs.map +1 -0
- package/dist/esm/hooks/useSiweKeycloak.native.js +36 -0
- package/dist/esm/hooks/useSiweKeycloak.native.js.map +1 -0
- package/dist/esm/index.mjs +107 -0
- package/dist/esm/index.mjs.map +1 -0
- package/dist/esm/index.native.js +160 -0
- package/dist/esm/index.native.js.map +1 -0
- package/dist/esm/render.mjs +42 -0
- package/dist/esm/render.mjs.map +1 -0
- package/dist/esm/render.native.js +43 -0
- package/dist/esm/render.native.js.map +1 -0
- package/dist/esm/siwe.mjs +51 -0
- package/dist/esm/siwe.mjs.map +1 -0
- package/dist/esm/siwe.native.js +66 -0
- package/dist/esm/siwe.native.js.map +1 -0
- package/dist/esm/types.mjs +2 -0
- package/dist/esm/types.mjs.map +1 -0
- package/dist/esm/types.native.js +2 -0
- package/dist/esm/types.native.js.map +1 -0
- package/dist/esm/util.mjs +27 -0
- package/dist/esm/util.mjs.map +1 -0
- package/dist/esm/util.native.js +27 -0
- package/dist/esm/util.native.js.map +1 -0
- package/dist/esm/util.spec.mjs +54 -0
- package/dist/esm/util.spec.mjs.map +1 -0
- package/dist/esm/util.spec.native.js +60 -0
- package/dist/esm/util.spec.native.js.map +1 -0
- package/package.json +81 -0
- package/src/SiweKeycloakProvider.tsx +297 -0
- package/src/base64.spec.ts +65 -0
- package/src/base64.ts +26 -0
- package/src/context.ts +10 -0
- package/src/hooks/context.ts +10 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useSiweKeycloak.ts +23 -0
- package/src/index.ts +127 -0
- package/src/main/java/com/bitspur/keycloak/siwe/Errors.java +21 -0
- package/src/main/java/com/bitspur/keycloak/siwe/LiteAuthenticationService.java +167 -0
- package/src/main/java/com/bitspur/keycloak/siwe/SiweCredentials.java +36 -0
- package/src/main/java/com/bitspur/keycloak/siwe/SiweSession.java +36 -0
- package/src/main/java/com/bitspur/keycloak/siwe/SiweUtil.java +599 -0
- package/src/main/java/com/bitspur/keycloak/siwe/Token.java +85 -0
- package/src/main/java/com/bitspur/keycloak/siwe/authenticators/browser/SiweBrowserAuthenticator.java +109 -0
- package/src/main/java/com/bitspur/keycloak/siwe/authenticators/browser/SiweBrowserAuthenticatorFactory.java +93 -0
- package/src/main/java/com/bitspur/keycloak/siwe/authenticators/directgrant/SiweDirectGrantAuthenticator.java +153 -0
- package/src/main/java/com/bitspur/keycloak/siwe/authenticators/directgrant/SiweDirectGrantAuthenticatorFactory.java +93 -0
- package/src/main/java/com/bitspur/keycloak/siwe/jpa/nonce/SiweNonce.java +95 -0
- package/src/main/java/com/bitspur/keycloak/siwe/jpa/nonce/SiweNonceJpaEntityProvider.java +82 -0
- package/src/main/java/com/bitspur/keycloak/siwe/jpa/nonce/SiweNonceJpaEntityProviderFactory.java +46 -0
- package/src/main/java/com/bitspur/keycloak/siwe/jpa/wallet/SiweWalletAddress.java +103 -0
- package/src/main/java/com/bitspur/keycloak/siwe/jpa/wallet/SiweWalletJpaEntityProvider.java +43 -0
- package/src/main/java/com/bitspur/keycloak/siwe/jpa/wallet/SiweWalletJpaEntityProviderFactory.java +46 -0
- package/src/main/java/com/bitspur/keycloak/siwe/protocol/SiweWalletProtocolMapper.java +85 -0
- package/src/main/java/com/bitspur/keycloak/siwe/resource/SiweResource.java +210 -0
- package/src/main/java/com/bitspur/keycloak/siwe/resource/SiweResourceProvider.java +34 -0
- package/src/main/java/com/bitspur/keycloak/siwe/resource/SiweResourceProviderFactory.java +46 -0
- package/src/main/java/com/bitspur/keycloak/siwe/siu/SiuCredentials.java +52 -0
- package/src/main/java/com/bitspur/keycloak/siwe/siu/SiuMessage.java +362 -0
- package/src/main/java/com/bitspur/keycloak/siwe/siu/SiuParseException.java +23 -0
- package/src/main/java/com/bitspur/keycloak/siwe/siu/SiuUtil.java +547 -0
- package/src/main/java/com/bitspur/keycloak/siwe/siu/authenticators/browser/SiuBrowserAuthenticator.java +112 -0
- package/src/main/java/com/bitspur/keycloak/siwe/siu/authenticators/browser/SiuBrowserAuthenticatorFactory.java +96 -0
- package/src/main/java/com/bitspur/keycloak/siwe/siu/authenticators/directgrant/SiuDirectGrantAuthenticator.java +169 -0
- package/src/main/java/com/bitspur/keycloak/siwe/siu/authenticators/directgrant/SiuDirectGrantAuthenticatorFactory.java +96 -0
- package/src/main/java/com/bitspur/keycloak/siwe/siu/resource/SiuResource.java +244 -0
- package/src/main/java/com/bitspur/keycloak/siwe/siu/resource/SiuResourceProvider.java +34 -0
- package/src/main/java/com/bitspur/keycloak/siwe/siu/resource/SiuResourceProviderFactory.java +48 -0
- package/src/main/java/com/bitspur/keycloak/siwe/siws/SiwsCredentials.java +52 -0
- package/src/main/java/com/bitspur/keycloak/siwe/siws/SiwsMessage.java +362 -0
- package/src/main/java/com/bitspur/keycloak/siwe/siws/SiwsParseException.java +23 -0
- package/src/main/java/com/bitspur/keycloak/siwe/siws/SiwsUtil.java +564 -0
- package/src/main/java/com/bitspur/keycloak/siwe/siws/authenticators/browser/SiwsBrowserAuthenticator.java +112 -0
- package/src/main/java/com/bitspur/keycloak/siwe/siws/authenticators/browser/SiwsBrowserAuthenticatorFactory.java +96 -0
- package/src/main/java/com/bitspur/keycloak/siwe/siws/authenticators/directgrant/SiwsDirectGrantAuthenticator.java +169 -0
- package/src/main/java/com/bitspur/keycloak/siwe/siws/authenticators/directgrant/SiwsDirectGrantAuthenticatorFactory.java +96 -0
- package/src/main/java/com/bitspur/keycloak/siwe/siws/resource/SiwsResource.java +244 -0
- package/src/main/java/com/bitspur/keycloak/siwe/siws/resource/SiwsResourceProvider.java +34 -0
- package/src/main/java/com/bitspur/keycloak/siwe/siws/resource/SiwsResourceProviderFactory.java +48 -0
- package/src/main/resources/META-INF/keycloak-themes.json +8 -0
- package/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory +6 -0
- package/src/main/resources/META-INF/services/org.keycloak.connections.jpa.entityprovider.JpaEntityProviderFactory +2 -0
- package/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper +1 -0
- package/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory +3 -0
- package/src/main/resources/META-INF/siwe-nonce-changelog.xml +29 -0
- package/src/main/resources/META-INF/siwe-wallet-changelog.xml +46 -0
- package/src/render.tsx +47 -0
- package/src/siwe.tsx +57 -0
- package/src/types.ts +6 -0
- package/src/util.spec.ts +72 -0
- package/src/util.ts +28 -0
package/src/main/java/com/bitspur/keycloak/siwe/authenticators/browser/SiweBrowserAuthenticator.java
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File: /src/main/java/com/bitspur/keycloak/siwe/authenticators/browser/SiweBrowserAuthenticator.java
|
|
3
|
+
* Project: browser
|
|
4
|
+
* File Created: 20-03-2022 02:51:33
|
|
5
|
+
* Author: Clay Risser
|
|
6
|
+
* -----
|
|
7
|
+
* Last Modified: 10-01-2025 00:06:46
|
|
8
|
+
* Modified By: Clay Risser
|
|
9
|
+
* -----
|
|
10
|
+
* BitSpur (c) Copyright 2022
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
package com.bitspur.keycloak.siwe.authenticators.browser;
|
|
14
|
+
|
|
15
|
+
import jakarta.ws.rs.core.MultivaluedMap;
|
|
16
|
+
|
|
17
|
+
import org.jboss.logging.Logger;
|
|
18
|
+
import org.keycloak.authentication.AuthenticationFlowContext;
|
|
19
|
+
import org.keycloak.authentication.AuthenticationFlowError;
|
|
20
|
+
import org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator;
|
|
21
|
+
import org.keycloak.events.Errors;
|
|
22
|
+
import org.keycloak.forms.login.LoginFormsProvider;
|
|
23
|
+
import org.keycloak.models.KeycloakSession;
|
|
24
|
+
import org.keycloak.models.RealmModel;
|
|
25
|
+
import org.keycloak.models.UserModel;
|
|
26
|
+
import org.keycloak.representations.idm.CredentialRepresentation;
|
|
27
|
+
import org.keycloak.services.managers.AuthenticationManager;
|
|
28
|
+
|
|
29
|
+
import com.bitspur.keycloak.siwe.SiweCredentials;
|
|
30
|
+
import com.bitspur.keycloak.siwe.SiweUtil;
|
|
31
|
+
import com.bitspur.keycloak.siwe.Errors.SiweKeycloakException;
|
|
32
|
+
|
|
33
|
+
public class SiweBrowserAuthenticator extends AbstractUsernameFormAuthenticator {
|
|
34
|
+
private static final Logger logger = Logger.getLogger(SiweBrowserAuthenticator.class);
|
|
35
|
+
|
|
36
|
+
@Override
|
|
37
|
+
public void authenticate(AuthenticationFlowContext context) {
|
|
38
|
+
LoginFormsProvider form = context.form();
|
|
39
|
+
context.challenge(SiweUtil.createLoginSiwePage(form));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@Override
|
|
43
|
+
public void action(AuthenticationFlowContext context) {
|
|
44
|
+
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
|
|
45
|
+
if (formData.containsKey("cancel")) {
|
|
46
|
+
context.cancelLogin();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
String password = this.retrievePassword(context);
|
|
50
|
+
String username = this.retrieveUsername(context);
|
|
51
|
+
try {
|
|
52
|
+
SiweCredentials siweCredentials = new SiweCredentials(username, password);
|
|
53
|
+
UserModel user = SiweUtil.findOrCreateUser(context, siweCredentials);
|
|
54
|
+
if (user == null) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
context.setUser(user);
|
|
58
|
+
if (!SiweUtil.verify(context.getSession(), context.getAuthenticatorConfig(), siweCredentials)) {
|
|
59
|
+
context.attempted();
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
} catch (SiweKeycloakException e) {
|
|
63
|
+
context.getEvent().user(context.getUser());
|
|
64
|
+
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
|
|
65
|
+
LoginFormsProvider form = context.form();
|
|
66
|
+
logger.error(e);
|
|
67
|
+
context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS,
|
|
68
|
+
SiweUtil.createLoginSiwePage(form));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
context.success();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@Override
|
|
75
|
+
public boolean requiresUser() {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@Override
|
|
80
|
+
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@Override
|
|
85
|
+
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@Override
|
|
89
|
+
public void close() {
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
protected String retrievePassword(AuthenticationFlowContext context) {
|
|
93
|
+
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
|
|
94
|
+
String password = formData.getFirst(CredentialRepresentation.PASSWORD);
|
|
95
|
+
if (password == null) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
return password.trim();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
protected String retrieveUsername(AuthenticationFlowContext context) {
|
|
102
|
+
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
|
|
103
|
+
String username = formData.getFirst(AuthenticationManager.FORM_USERNAME);
|
|
104
|
+
if (username == null) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
return username.trim();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File: /src/main/java/com/bitspur/keycloak/siwe/authenticators/browser/SiweBrowserAuthenticatorFactory.java
|
|
3
|
+
* Project: browser
|
|
4
|
+
* File Created: 20-03-2022 02:51:14
|
|
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
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
package com.bitspur.keycloak.siwe.authenticators.browser;
|
|
14
|
+
|
|
15
|
+
import java.util.Arrays;
|
|
16
|
+
import java.util.List;
|
|
17
|
+
|
|
18
|
+
import org.keycloak.Config;
|
|
19
|
+
import org.keycloak.authentication.Authenticator;
|
|
20
|
+
import org.keycloak.authentication.AuthenticatorFactory;
|
|
21
|
+
import org.keycloak.models.AuthenticationExecutionModel;
|
|
22
|
+
import org.keycloak.models.KeycloakSession;
|
|
23
|
+
import org.keycloak.models.KeycloakSessionFactory;
|
|
24
|
+
import org.keycloak.provider.ProviderConfigProperty;
|
|
25
|
+
|
|
26
|
+
import com.bitspur.keycloak.siwe.SiweUtil;
|
|
27
|
+
|
|
28
|
+
public class SiweBrowserAuthenticatorFactory implements AuthenticatorFactory {
|
|
29
|
+
public static final String ID = "siwe-browser-authenticator";
|
|
30
|
+
|
|
31
|
+
@Override
|
|
32
|
+
public Authenticator create(KeycloakSession keycloakSession) {
|
|
33
|
+
return new SiweBrowserAuthenticator();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@Override
|
|
37
|
+
public String getDisplayType() {
|
|
38
|
+
return "Siwe Browser Authenticator";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@Override
|
|
42
|
+
public boolean isConfigurable() {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@Override
|
|
47
|
+
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
|
|
48
|
+
return new AuthenticationExecutionModel.Requirement[] {
|
|
49
|
+
AuthenticationExecutionModel.Requirement.REQUIRED,
|
|
50
|
+
AuthenticationExecutionModel.Requirement.ALTERNATIVE,
|
|
51
|
+
AuthenticationExecutionModel.Requirement.DISABLED,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@Override
|
|
56
|
+
public boolean isUserSetupAllowed() {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@Override
|
|
61
|
+
public String getHelpText() {
|
|
62
|
+
return "";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@Override
|
|
66
|
+
public List<ProviderConfigProperty> getConfigProperties() {
|
|
67
|
+
return Arrays.asList(
|
|
68
|
+
SiweUtil.getJsonRpcUrlConfigProperty(),
|
|
69
|
+
SiweUtil.getRequiredTokensConfigProperty());
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@Override
|
|
73
|
+
public String getReferenceCategory() {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@Override
|
|
78
|
+
public void init(Config.Scope scope) {
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@Override
|
|
82
|
+
public void postInit(KeycloakSessionFactory keycloakSessionFactory) {
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@Override
|
|
86
|
+
public void close() {
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@Override
|
|
90
|
+
public String getId() {
|
|
91
|
+
return SiweBrowserAuthenticatorFactory.ID;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File: /src/main/java/com/bitspur/keycloak/siwe/authenticators/directgrant/SiweDirectGrantAuthenticator.java
|
|
3
|
+
* Project: directgrant
|
|
4
|
+
* File Created: 21-03-2022 09:50:11
|
|
5
|
+
* Author: Clay Risser
|
|
6
|
+
* -----
|
|
7
|
+
* Last Modified: 09-01-2025 23:48:08
|
|
8
|
+
* Modified By: Clay Risser
|
|
9
|
+
* -----
|
|
10
|
+
* BitSpur (c) Copyright 2022
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
package com.bitspur.keycloak.siwe.authenticators.directgrant;
|
|
14
|
+
|
|
15
|
+
import jakarta.ws.rs.core.MediaType;
|
|
16
|
+
import jakarta.ws.rs.core.MultivaluedMap;
|
|
17
|
+
import jakarta.ws.rs.core.Response;
|
|
18
|
+
|
|
19
|
+
import static org.keycloak.authentication.authenticators.util.AuthenticatorUtils.getDisabledByBruteForceEventError;
|
|
20
|
+
|
|
21
|
+
import org.jboss.logging.Logger;
|
|
22
|
+
import org.keycloak.authentication.AuthenticationFlowContext;
|
|
23
|
+
import org.keycloak.authentication.AuthenticationFlowError;
|
|
24
|
+
import org.keycloak.authentication.Authenticator;
|
|
25
|
+
import org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator;
|
|
26
|
+
import org.keycloak.events.Details;
|
|
27
|
+
import org.keycloak.events.Errors;
|
|
28
|
+
import org.keycloak.http.HttpRequest;
|
|
29
|
+
import org.keycloak.models.KeycloakSession;
|
|
30
|
+
import org.keycloak.models.RealmModel;
|
|
31
|
+
import org.keycloak.models.UserModel;
|
|
32
|
+
import org.keycloak.representations.idm.CredentialRepresentation;
|
|
33
|
+
import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
|
|
34
|
+
import org.keycloak.services.managers.AuthenticationManager;
|
|
35
|
+
|
|
36
|
+
import com.bitspur.keycloak.siwe.SiweCredentials;
|
|
37
|
+
import com.bitspur.keycloak.siwe.SiweUtil;
|
|
38
|
+
import com.bitspur.keycloak.siwe.Errors.SiweKeycloakException;;
|
|
39
|
+
|
|
40
|
+
public class SiweDirectGrantAuthenticator implements Authenticator {
|
|
41
|
+
|
|
42
|
+
Logger logger = Logger.getLogger(SiweDirectGrantAuthenticator.class);
|
|
43
|
+
|
|
44
|
+
@Override
|
|
45
|
+
public void authenticate(AuthenticationFlowContext context) {
|
|
46
|
+
String password = retrievePassword(context);
|
|
47
|
+
String username = retrieveUsername(context);
|
|
48
|
+
KeycloakSession session = context.getSession();
|
|
49
|
+
RealmModel realm = context.getRealm();
|
|
50
|
+
if (username == null) {
|
|
51
|
+
context.getEvent().error(Errors.USER_NOT_FOUND);
|
|
52
|
+
Response challengeResponse = this.errorResponse(Response.Status.UNAUTHORIZED, "invalid_request",
|
|
53
|
+
"Missing parameter: username");
|
|
54
|
+
context.failure(AuthenticationFlowError.INVALID_USER, challengeResponse);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
SiweCredentials siweCredentials = new SiweCredentials(username, password);
|
|
59
|
+
String address = siweCredentials.message.getAddress();
|
|
60
|
+
context.getEvent().detail(Details.USERNAME, address);
|
|
61
|
+
context.getAuthenticationSession().setAuthNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME, address);
|
|
62
|
+
UserModel user = SiweUtil.findOrCreateUser(context, siweCredentials, false);
|
|
63
|
+
if (user == null) {
|
|
64
|
+
context.getEvent().error(Errors.USER_NOT_FOUND);
|
|
65
|
+
Response challengeResponse = this.errorResponse(Response.Status.UNAUTHORIZED, "invalid_grant",
|
|
66
|
+
"Invalid user credentials");
|
|
67
|
+
context.failure(AuthenticationFlowError.INVALID_USER, challengeResponse);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
String bruteForceError = getDisabledByBruteForceEventError(context.getProtector(), session, realm, user);
|
|
71
|
+
if (bruteForceError != null) {
|
|
72
|
+
context.getEvent().user(user);
|
|
73
|
+
context.getEvent().error(bruteForceError);
|
|
74
|
+
Response challengeResponse = this.errorResponse(Response.Status.UNAUTHORIZED, "invalid_grant",
|
|
75
|
+
"Invalid user credentials");
|
|
76
|
+
context.forceChallenge(challengeResponse);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (!user.isEnabled()) {
|
|
80
|
+
context.getEvent().user(user);
|
|
81
|
+
context.getEvent().error(Errors.USER_DISABLED);
|
|
82
|
+
Response challengeResponse = this.errorResponse(Response.Status.BAD_REQUEST, "invalid_grant",
|
|
83
|
+
"Account disabled");
|
|
84
|
+
context.forceChallenge(challengeResponse);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
context.setUser(user);
|
|
88
|
+
if (!SiweUtil.verify(context.getSession(), context.getAuthenticatorConfig(), siweCredentials)) {
|
|
89
|
+
context.getEvent().user(context.getUser());
|
|
90
|
+
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
|
|
91
|
+
Response challengeResponse = this.errorResponse(Response.Status.UNAUTHORIZED, "invalid_grant",
|
|
92
|
+
"Invalid user credentials");
|
|
93
|
+
context.failure(AuthenticationFlowError.INVALID_CREDENTIALS, challengeResponse);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
} catch (SiweKeycloakException e) {
|
|
97
|
+
logger.error(e);
|
|
98
|
+
context.getEvent().user(context.getUser());
|
|
99
|
+
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
|
|
100
|
+
Response challengeResponse = this.errorResponse(Response.Status.UNAUTHORIZED, "invalid_grant",
|
|
101
|
+
e.getMessage());
|
|
102
|
+
context.failure(AuthenticationFlowError.INVALID_CREDENTIALS, challengeResponse);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
context.success();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@Override
|
|
109
|
+
public void action(AuthenticationFlowContext context) {
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@Override
|
|
113
|
+
public boolean requiresUser() {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@Override
|
|
118
|
+
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@Override
|
|
123
|
+
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@Override
|
|
127
|
+
public void close() {
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
protected String retrieveUsername(AuthenticationFlowContext context) {
|
|
131
|
+
HttpRequest request = context.getHttpRequest();
|
|
132
|
+
MultivaluedMap<String, String> formData = request.getDecodedFormParameters();
|
|
133
|
+
String username = formData.getFirst(AuthenticationManager.FORM_USERNAME);
|
|
134
|
+
if (username == null) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
return username.trim();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
protected String retrievePassword(AuthenticationFlowContext context) {
|
|
141
|
+
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
|
|
142
|
+
String password = formData.getFirst(CredentialRepresentation.PASSWORD);
|
|
143
|
+
if (password == null) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
return password.trim();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
protected Response errorResponse(Response.Status status, String error, String errorDescription) {
|
|
150
|
+
OAuth2ErrorRepresentation errorRep = new OAuth2ErrorRepresentation(error, errorDescription);
|
|
151
|
+
return Response.status(status).entity(errorRep).type(MediaType.APPLICATION_JSON_TYPE).build();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File: /src/main/java/com/bitspur/keycloak/siwe/authenticators/directgrant/SiweDirectGrantAuthenticatorFactory.java
|
|
3
|
+
* Project: directgrant
|
|
4
|
+
* File Created: 21-03-2022 09:50:49
|
|
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
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
package com.bitspur.keycloak.siwe.authenticators.directgrant;
|
|
14
|
+
|
|
15
|
+
import java.util.Arrays;
|
|
16
|
+
import java.util.List;
|
|
17
|
+
|
|
18
|
+
import org.keycloak.Config;
|
|
19
|
+
import org.keycloak.authentication.Authenticator;
|
|
20
|
+
import org.keycloak.authentication.AuthenticatorFactory;
|
|
21
|
+
import org.keycloak.models.AuthenticationExecutionModel;
|
|
22
|
+
import org.keycloak.models.KeycloakSession;
|
|
23
|
+
import org.keycloak.models.KeycloakSessionFactory;
|
|
24
|
+
import org.keycloak.provider.ProviderConfigProperty;
|
|
25
|
+
|
|
26
|
+
import com.bitspur.keycloak.siwe.SiweUtil;
|
|
27
|
+
|
|
28
|
+
public class SiweDirectGrantAuthenticatorFactory implements AuthenticatorFactory {
|
|
29
|
+
public static final String ID = "siwe-direct-grant-authenticator";
|
|
30
|
+
|
|
31
|
+
@Override
|
|
32
|
+
public Authenticator create(KeycloakSession keycloakSession) {
|
|
33
|
+
return new SiweDirectGrantAuthenticator();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@Override
|
|
37
|
+
public String getDisplayType() {
|
|
38
|
+
return "Siwe Direct Grant Authenticator";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@Override
|
|
42
|
+
public boolean isConfigurable() {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@Override
|
|
47
|
+
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
|
|
48
|
+
return new AuthenticationExecutionModel.Requirement[] {
|
|
49
|
+
AuthenticationExecutionModel.Requirement.REQUIRED,
|
|
50
|
+
AuthenticationExecutionModel.Requirement.ALTERNATIVE,
|
|
51
|
+
AuthenticationExecutionModel.Requirement.DISABLED,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@Override
|
|
56
|
+
public boolean isUserSetupAllowed() {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@Override
|
|
61
|
+
public String getHelpText() {
|
|
62
|
+
return "";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@Override
|
|
66
|
+
public List<ProviderConfigProperty> getConfigProperties() {
|
|
67
|
+
return Arrays.asList(
|
|
68
|
+
SiweUtil.getJsonRpcUrlConfigProperty(),
|
|
69
|
+
SiweUtil.getRequiredTokensConfigProperty());
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@Override
|
|
73
|
+
public String getReferenceCategory() {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@Override
|
|
78
|
+
public void init(Config.Scope scope) {
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@Override
|
|
82
|
+
public void postInit(KeycloakSessionFactory keycloakSessionFactory) {
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@Override
|
|
86
|
+
public void close() {
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@Override
|
|
90
|
+
public String getId() {
|
|
91
|
+
return ID;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File: /src/main/java/com/bitspur/keycloak/siwe/jpa/nonce/SiweNonce.java
|
|
3
|
+
* Project: jpa
|
|
4
|
+
* File Created: 09-04-2023 10:40:57
|
|
5
|
+
* Author: Clay Risser
|
|
6
|
+
* -----
|
|
7
|
+
* Last Modified: 12-02-2024 12:32:34
|
|
8
|
+
* Modified By: Clay Risser
|
|
9
|
+
* -----
|
|
10
|
+
* BitSpur (c) Copyright 2022 - 2023
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
package com.bitspur.keycloak.siwe.jpa.nonce;
|
|
14
|
+
|
|
15
|
+
import java.time.LocalDateTime;
|
|
16
|
+
|
|
17
|
+
import jakarta.persistence.Column;
|
|
18
|
+
import jakarta.persistence.Entity;
|
|
19
|
+
import jakarta.persistence.GeneratedValue;
|
|
20
|
+
import jakarta.persistence.GenerationType;
|
|
21
|
+
import jakarta.persistence.Id;
|
|
22
|
+
import jakarta.persistence.NamedQueries;
|
|
23
|
+
import jakarta.persistence.NamedQuery;
|
|
24
|
+
import jakarta.persistence.Table;
|
|
25
|
+
|
|
26
|
+
import org.hibernate.annotations.CreationTimestamp;
|
|
27
|
+
import org.hibernate.annotations.UpdateTimestamp;
|
|
28
|
+
|
|
29
|
+
@Entity
|
|
30
|
+
@Table(name = "SIWE_NONCE")
|
|
31
|
+
@NamedQueries({
|
|
32
|
+
@NamedQuery(name = "findByRealm", query = "from SiweNonce where realmId = :realmId"),
|
|
33
|
+
@NamedQuery(name = "findByKey", query = "from SiweNonce where key = :key"),
|
|
34
|
+
@NamedQuery(name = "findByRealmKey", query = "from SiweNonce where realmId = :realmId and key = :key"),
|
|
35
|
+
@NamedQuery(name = "findOlderThan", query = "from SiweNonce where createdAt < :createdAt")
|
|
36
|
+
})
|
|
37
|
+
public class SiweNonce {
|
|
38
|
+
@Id
|
|
39
|
+
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
40
|
+
@Column(name = "ID", nullable = false)
|
|
41
|
+
private Long id;
|
|
42
|
+
|
|
43
|
+
@Column(name = "NONCE_KEY", nullable = false)
|
|
44
|
+
private String key;
|
|
45
|
+
|
|
46
|
+
@Column(name = "NONCE", nullable = true)
|
|
47
|
+
private String nonce;
|
|
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 getKey() {
|
|
65
|
+
return this.key;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public String getNonce() {
|
|
69
|
+
return this.nonce;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public String getRealmId() {
|
|
73
|
+
return this.realmId;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public LocalDateTime getCreatedAt() {
|
|
77
|
+
return this.createdAt;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public LocalDateTime getUpdatedAt() {
|
|
81
|
+
return this.updatedAt;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public void setKey(String key) {
|
|
85
|
+
this.key = key;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public void setNonce(String nonce) {
|
|
89
|
+
this.nonce = nonce;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
public void setRealmId(String realmId) {
|
|
93
|
+
this.realmId = realmId;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* File: /src/main/java/com/bitspur/keycloak/siwe/jpa/nonce/SiweNonceJpaEntityProvider.java
|
|
4
|
+
* Project: authenticators
|
|
5
|
+
* File Created: 09-04-2023 10:30:42
|
|
6
|
+
* Author: Clay Risser
|
|
7
|
+
* -----
|
|
8
|
+
* Last Modified: 12-02-2024 12:33:17
|
|
9
|
+
* Modified By: Clay Risser
|
|
10
|
+
* -----
|
|
11
|
+
* BitSpur (c) Copyright 2022 - 2023
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
package com.bitspur.keycloak.siwe.jpa.nonce;
|
|
15
|
+
|
|
16
|
+
import org.jboss.logging.Logger;
|
|
17
|
+
import org.keycloak.connections.jpa.JpaConnectionProvider;
|
|
18
|
+
import org.keycloak.connections.jpa.entityprovider.JpaEntityProvider;
|
|
19
|
+
import org.keycloak.models.KeycloakSession;
|
|
20
|
+
import org.keycloak.services.scheduled.ScheduledTaskRunner;
|
|
21
|
+
import org.keycloak.timer.ScheduledTask;
|
|
22
|
+
import org.keycloak.timer.TimerProvider;
|
|
23
|
+
|
|
24
|
+
import java.time.Duration;
|
|
25
|
+
import java.time.LocalDateTime;
|
|
26
|
+
import java.util.Collections;
|
|
27
|
+
import java.util.List;
|
|
28
|
+
|
|
29
|
+
import jakarta.persistence.EntityManager;
|
|
30
|
+
|
|
31
|
+
public class SiweNonceJpaEntityProvider implements JpaEntityProvider {
|
|
32
|
+
KeycloakSession session;
|
|
33
|
+
private final Logger logger = Logger.getLogger(SiweNonceJpaEntityProvider.class);
|
|
34
|
+
|
|
35
|
+
SiweNonceJpaEntityProvider(KeycloakSession session) {
|
|
36
|
+
this.session = session;
|
|
37
|
+
this.scheduleTask();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@Override
|
|
41
|
+
public List<Class<?>> getEntities() {
|
|
42
|
+
return Collections.<Class<?>>singletonList(SiweNonce.class);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@Override
|
|
46
|
+
public String getChangelogLocation() {
|
|
47
|
+
return "META-INF/siwe-nonce-changelog.xml";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@Override
|
|
51
|
+
public void close() {
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@Override
|
|
55
|
+
public String getFactoryId() {
|
|
56
|
+
return SiweNonceJpaEntityProviderFactory.ID;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public void scheduleTask() {
|
|
60
|
+
TimerProvider timer = session.getProvider(TimerProvider.class);
|
|
61
|
+
ScheduledTask task = new ScheduledTask() {
|
|
62
|
+
public void run(KeycloakSession session) {
|
|
63
|
+
Integer removedCount = removeOlderThan(LocalDateTime.now().minus(Duration.ofMinutes(10)));
|
|
64
|
+
if (removedCount > 0) {
|
|
65
|
+
logger.info("Cleaned up " + removedCount + " nonces");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
ScheduledTaskRunner runner = new ScheduledTaskRunner(session.getKeycloakSessionFactory(), task);
|
|
70
|
+
timer.schedule(runner, Duration.ofMinutes(5).toMillis(), "siwe-cleanup");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private Integer removeOlderThan(LocalDateTime createdAt) {
|
|
74
|
+
EntityManager entityManager = session.getProvider(JpaConnectionProvider.class).getEntityManager();
|
|
75
|
+
List<SiweNonce> nonces = entityManager.createNamedQuery("findOlderThan", SiweNonce.class)
|
|
76
|
+
.setParameter("createdAt", createdAt).getResultList();
|
|
77
|
+
for (SiweNonce nonce : nonces) {
|
|
78
|
+
entityManager.remove(nonce);
|
|
79
|
+
}
|
|
80
|
+
return nonces.size();
|
|
81
|
+
}
|
|
82
|
+
}
|
package/src/main/java/com/bitspur/keycloak/siwe/jpa/nonce/SiweNonceJpaEntityProviderFactory.java
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File: /src/main/java/com/bitspur/keycloak/siwe/jpa/nonce/SiweNonceJpaEntityProviderFactory.java
|
|
3
|
+
* Project: jpa
|
|
4
|
+
* File Created: 09-04-2023 10:33:19
|
|
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.jpa.nonce;
|
|
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 SiweNonceJpaEntityProviderFactory implements JpaEntityProviderFactory {
|
|
22
|
+
|
|
23
|
+
protected static final String ID = "siwe-nonce-entity-provider";
|
|
24
|
+
|
|
25
|
+
@Override
|
|
26
|
+
public JpaEntityProvider create(KeycloakSession session) {
|
|
27
|
+
return new SiweNonceJpaEntityProvider(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
|
+
}
|