Changeset 6754
- Timestamp:
- May 31, 2022, 12:01:30 PM (16 months ago)
- Location:
- extensions/net.sf.basedb.webauthn/trunk
- Files:
-
- 1 added
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
extensions/net.sf.basedb.webauthn/trunk/src/net/sf/basedb/webauthn/PreLoginAuthenticationManager.java
r6752 r6754 1 1 package net.sf.basedb.webauthn; 2 2 3 import java.util.Collections; 4 import java.util.Optional; 5 import java.util.Set; 3 import com.yubico.webauthn.AssertionRequest; 6 4 7 8 import com.yubico.webauthn.AssertionRequest;9 import com.yubico.webauthn.CredentialRepository;10 import com.yubico.webauthn.RegisteredCredential;11 import com.yubico.webauthn.RelyingParty;12 import com.yubico.webauthn.StartAssertionOptions;13 import com.yubico.webauthn.data.ByteArray;14 import com.yubico.webauthn.data.PublicKeyCredentialDescriptor;15 import com.yubico.webauthn.data.RelyingPartyIdentity;16 17 import net.sf.basedb.core.Application;18 5 import net.sf.basedb.core.AuthenticationContext; 19 6 import net.sf.basedb.core.authentication.AuthenticatedUser; … … 21 8 import net.sf.basedb.core.authentication.LoginException; 22 9 import net.sf.basedb.core.authentication.LoginRequest; 10 import net.sf.basedb.core.authentication.UnknownLoginException; 23 11 import net.sf.basedb.core.data.UserData; 24 12 … … 52 40 String login = request.getLogin(); 53 41 54 // Check if the user exists 42 // Check if the user exists and has configured a security key 55 43 UserData user = context.getUserByLogin(login); 56 if (user == null) 57 { 58 throw new LoginException("Unknown username '" + login + "'."); 59 } 44 if (user == null) throw new UnknownLoginException(login); 60 45 if (user.getExtended("webAuthnCredentialId") == null) 61 46 { 62 throw new LoginException("User '"+login+"' has not configured a security key.");47 throw new LoginException("User '"+login+"' has not configured a WebAuthn Security Key."); 63 48 } 64 49 65 RelyingPartyIdentity rpi = RelyingPartyIdentity.builder() 66 .id(request.getAttribute("serverName")) // TODO -- config option 67 .name(Application.getTitle()) 68 .build(); 69 70 RelyingParty rp = RelyingParty.builder() 71 .identity(rpi) 72 .credentialRepository(new NoCredentialRepository(user)) 73 .allowOriginPort(true) // TODO -- config option? 74 .build(); 75 76 AssertionRequest assertionRequest = rp.startAssertion(StartAssertionOptions.builder() 77 .username(login) 78 .build()); 50 String serverName = request.getAttribute("serverName"); 51 LoginProcessHandler handler = new LoginProcessHandler(user, serverName); 52 AssertionRequest assertionRequest = handler.getAssertionRequest(); 79 53 80 context.getSessionControl().setSessionSetting("webauthn-assertion-request", assertionRequest); 81 54 context.getSessionControl().setSessionSetting("webauthn-login-handler", handler); 82 55 throw new AssertionRequestException(assertionRequest); 83 }84 85 86 static class NoCredentialRepository87 implements CredentialRepository88 {89 90 private final UserData user;91 92 NoCredentialRepository(UserData user)93 {94 this.user = user;95 }96 97 @Override98 public Set<PublicKeyCredentialDescriptor> getCredentialIdsForUsername(String username)99 {100 System.out.println("getCredentialIdsForUsername: "+username);101 String credentialId = (String)user.getExtended("webAuthnCredentialId");102 PublicKeyCredentialDescriptor pk = PublicKeyCredentialDescriptor.builder()103 .id(ByteArray.fromBase64(credentialId))104 .build();105 return Collections.singleton(pk);106 }107 108 @Override109 public Optional<ByteArray> getUserHandleForUsername(String username)110 {111 System.out.println("getUserHandleForUsername: "+username);112 String userHandle = (String)user.getExtended("webAuthnUserHandle");113 return Optional.of(ByteArray.fromBase64(userHandle));114 }115 116 @Override117 public Optional<String> getUsernameForUserHandle(ByteArray userHandle)118 {119 System.out.println("getUsernameForUserHandle: "+userHandle);120 return Optional.empty();121 }122 123 @Override124 public Optional<RegisteredCredential> lookup(ByteArray credentialId, ByteArray userHandle)125 {126 System.out.println("lookup: "+credentialId+"; "+userHandle);127 128 String publicKey = (String)user.getExtended("webAuthnPublicKey");129 Integer count = (Integer)user.getExtended("webAuthnSignatureCount");130 return Optional.of(RegisteredCredential.builder()131 .credentialId(credentialId)132 .userHandle(userHandle)133 .publicKeyCose(ByteArray.fromBase64(publicKey))134 .signatureCount(count == null ? 0 : count)135 .build());136 }137 138 @Override139 public Set<RegisteredCredential> lookupAll(ByteArray credentialId)140 {141 System.out.println("lookupAll: "+credentialId);142 return Collections.emptySet();143 }144 145 56 } 146 57 -
extensions/net.sf.basedb.webauthn/trunk/src/net/sf/basedb/webauthn/WebAuthn.java
r6753 r6754 9 9 import java.util.Set; 10 10 11 import com.yubico.webauthn.CredentialRepository; 12 import com.yubico.webauthn.RelyingParty; 13 import com.yubico.webauthn.data.RelyingPartyIdentity; 14 15 import net.sf.basedb.core.Application; 11 16 import net.sf.basedb.core.ConfigurationException; 12 17 import net.sf.basedb.core.authentication.AuthenticationMethod; 13 18 import net.sf.basedb.util.FileUtil; 19 import net.sf.basedb.util.Values; 14 20 15 21 /** … … 157 163 return allowedAuthenticationMethods.contains("*") || allowedAuthenticationMethods.contains(auth.getMethod()); 158 164 } 165 166 /** 167 Create a RelyingParty instance. 168 @param id The ID taken from HTTP request parameters 169 @param cred A credentials repository implementation 170 */ 171 public static RelyingParty createRelyingParty(String id, CredentialRepository cred) 172 { 173 Properties cfg = getConfig(true); 174 id = cfg.getProperty("relying-party-id", id); 175 boolean port = Values.getBoolean(cfg.getProperty("relying-party-allow-origin-port")); 176 boolean subdomain = Values.getBoolean(cfg.getProperty("relying-party-allow-origin-subdomain")); 177 boolean disableCounter = Values.getBoolean(cfg.getProperty("relying-party-disable-signature-counter")); 178 179 RelyingPartyIdentity rpi = RelyingPartyIdentity.builder() 180 .id(id) 181 .name(Application.getTitle()) 182 .build(); 183 184 RelyingParty rp = RelyingParty.builder() 185 .identity(rpi) 186 .credentialRepository(cred) 187 .allowOriginPort(port) 188 .allowOriginSubdomain(subdomain) 189 .validateSignatureCounter(!disableCounter) 190 .build(); 191 return rp; 192 } 159 193 160 194 } -
extensions/net.sf.basedb.webauthn/trunk/src/net/sf/basedb/webauthn/WebAuthnAuthenticationManager.java
r6753 r6754 1 1 package net.sf.basedb.webauthn; 2 2 3 import com.yubico.webauthn.AssertionRequest; 3 4 4 import com.yubico.webauthn.AssertionResult; 5 import com.yubico.webauthn.FinishAssertionOptions;6 import com.yubico.webauthn.RelyingParty;7 import com.yubico.webauthn.data.AuthenticatorAssertionResponse;8 import com.yubico.webauthn.data.ClientAssertionExtensionOutputs;9 import com.yubico.webauthn.data.PublicKeyCredential;10 import com.yubico.webauthn.data.RelyingPartyIdentity;11 5 12 import net.sf.basedb.core.Application;13 6 import net.sf.basedb.core.AuthenticationContext; 14 7 import net.sf.basedb.core.authentication.AuthenticatedUser; … … 17 10 import net.sf.basedb.core.authentication.LoginRequest; 18 11 import net.sf.basedb.core.data.UserData; 19 import net.sf.basedb.webauthn.PreLoginAuthenticationManager.NoCredentialRepository;20 12 21 13 /** 22 Authentication 14 Authentication manager for the final step in the WebAuthn authentication 15 process. It will first verify that the login+password is correct and 16 then verify the response from the security key. 23 17 24 18 @author nicklas … … 60 54 } 61 55 56 // Check the password with the internal authentication 62 57 AuthenticatedUser auth = context.verifyUserInternal(request); 63 58 UserData user = context.getUserById(auth.getInternalId()); … … 73 68 throw new LoginException("User '" + login + "' has not configured a WebAuthn Security Key."); 74 69 } 70 return null; 75 71 } 76 else 72 73 // Get and clear the stored login handler 74 LoginProcessHandler handler = context.getSessionControl().setSessionSetting("webauthn-login-handler", null); 75 if (handler == null) 77 76 { 78 79 try 80 { 81 PublicKeyCredential<AuthenticatorAssertionResponse, ClientAssertionExtensionOutputs> pkc = PublicKeyCredential.parseAssertionResponseJson(assertionResponse); 82 AssertionRequest ar = context.getSessionControl().getSessionSetting("webauthn-assertion-request"); 83 FinishAssertionOptions ass = FinishAssertionOptions.builder() 84 .request(ar) 85 .response(pkc) 86 .build(); 87 88 RelyingPartyIdentity rpi = RelyingPartyIdentity.builder() 89 .id("localhost") // TODO -- config option 90 .name(Application.getTitle()) 91 .build(); 92 93 RelyingParty rp = RelyingParty.builder() 94 .identity(rpi) 95 .credentialRepository(new NoCredentialRepository(user)) 96 .allowOriginPort(true) // TODO -- config option? 97 .build(); 98 99 AssertionResult asResult = rp.finishAssertion(ass); 100 101 if (!asResult.isSuccess()) 102 { 103 System.out.println("webautn no success"); 104 105 throw new LoginException("Login failed"); 106 } 107 Integer count = (Integer)user.getExtended("webAuthnSignatureCount"); 108 user.setExtended("webAuthnSignatureCount", count+1); 109 110 } 111 catch (Exception ex) 112 { 113 ex.printStackTrace(System.out); 114 throw new LoginException("Login failed", ex); 115 } 116 117 auth = new AuthenticatedUser(WebAuthn.AUTHENTICATION_METHOD, user); 77 throw new LoginException("No login handler exists for user '"+login+"'"); 118 78 } 79 AssertionResult result = handler.processAssertionResponse(assertionResponse); 80 user.setExtended("webAuthnSignatureCount", (int)result.getSignatureCount()); 81 auth = new AuthenticatedUser(WebAuthn.AUTHENTICATION_METHOD, user); 119 82 return auth; 120 83 } -
extensions/net.sf.basedb.webauthn/trunk/webauthn.properties
r6753 r6754 27 27 ## override the 'no-webauthn' or 'require-webauthn' settings. 28 28 # allow-other-authentication = 29 30 ## The ID of the RelyingParty is normally taken from the HTTP 31 ## request headers. If, for some reason, that doesn't work as 32 ## expected it is possible manually configure it here 33 # relying-party-id = localhost 34 35 ## A flag that can be set to allow the RelyingParty to match 36 ## against any port number. Should not be needed except for 37 ## developers that use non-standard ports. 38 # relying-party-allow-origin-port = 1 39 40 ## A flag that can be set to allow the RelyingParty to match 41 ## against any subdomain to the id. 42 # relying-party-allow-origin-subdomain = 1 43 44 ## A flag for disabling validation of the signature counter 45 ## This counter is an increasing number intended to prevent 46 ## replay attacks. It is recommended to keep this enabled, 47 ## unless it is causing problems with security keys that 48 ## don't support the counter 49 # relying-party-disable-signature-counter = 1
Note: See TracChangeset
for help on using the changeset viewer.