Skip to content

Commit

Permalink
Update U2F API to support one step authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
yurem committed Jan 20, 2016
1 parent 27c31ee commit d1e3021
Show file tree
Hide file tree
Showing 16 changed files with 449 additions and 133 deletions.
11 changes: 8 additions & 3 deletions Model/src/main/java/org/xdi/oxauth/model/util/Base64Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ public static String base64urlencode(byte[] arg) {
}

public static byte[] base64urldecode(String arg) throws IllegalArgumentException {
String s = arg;
String s = removePadding(arg);
return Base64.decodeBase64(s); // Standard base64 decoder
}

public static String removePadding(String base64UrlEncoded) {
String s = base64UrlEncoded;
s = s.replace('-', '+'); // 62nd char of encoding
s = s.replace('_', '/'); // 63rd char of encoding
switch (s.length() % 4) // Pad with trailing '='s
Expand All @@ -38,7 +43,7 @@ public static byte[] base64urldecode(String arg) throws IllegalArgumentException
default:
throw new IllegalArgumentException("Illegal base64url string.");
}
return Base64.decodeBase64(s); // Standard base64 decoder
}
return s;
}

}
325 changes: 244 additions & 81 deletions Server/integrations/oxpush2/oxPush2ExternalAuthenticator.py

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* oxAuth is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text.
*
* Copyright (c) 2014, Gluu
*/

package org.xdi.oxauth.exception.fido.u2f;

public class InvalidKeyHandleDeviceException extends Exception {

private static final long serialVersionUID = 4324358428668365475L;

public InvalidKeyHandleDeviceException(String message, Throwable cause) {
super(message, cause);
}

public InvalidKeyHandleDeviceException(String message) {
super(message);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package org.xdi.oxauth.model.fido.u2f;

import java.io.Serializable;
import java.util.Date;

import org.gluu.site.ldap.persistence.annotation.LdapAttribute;
import org.gluu.site.ldap.persistence.annotation.LdapJsonObject;
Expand All @@ -31,6 +32,12 @@ public AuthenticateRequestMessageLdap(AuthenticateRequestMessage authenticateReq
this.requestId = authenticateRequestMessage.getRequestId();
}

public AuthenticateRequestMessageLdap(String dn, String id, Date creationDate, String sessionState, String userName,
AuthenticateRequestMessage authenticateRequestMessage) {
super(dn, id, authenticateRequestMessage.getRequestId(), creationDate, sessionState, userName);
this.authenticateRequestMessage = authenticateRequestMessage;
}

public AuthenticateRequestMessage getAuthenticateRequestMessage() {
return authenticateRequestMessage;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public DeviceRegistration(String keyHandle, String publicKey, String attestation
}

public DeviceRegistration(String keyHandle, String publicKey, X509Certificate attestationCert, long counter) throws BadInputException {
this.keyHandle = keyHandle;
try {
String attestationCertDecoded = Base64Util.base64urlencode(attestationCert.getEncoded());
this.deviceRegistrationConfiguration = new DeviceRegistrationConfiguration(publicKey, attestationCertDecoded);
Expand Down Expand Up @@ -127,11 +128,11 @@ public void setKeyHandle(String keyHandle) {
this.keyHandle = keyHandle;
}

public Integer getKeyHandlHashCode() {
public Integer getKeyHandleHashCode() {
return keyHandleHashCode;
}

public void setKeyHandlHashCode(Integer keyHandleHashCode) {
public void setKeyHandleHashCode(Integer keyHandleHashCode) {
this.keyHandleHashCode = keyHandleHashCode;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package org.xdi.oxauth.model.fido.u2f;

import java.io.Serializable;
import java.util.Date;

import org.gluu.site.ldap.persistence.annotation.LdapAttribute;
import org.gluu.site.ldap.persistence.annotation.LdapJsonObject;
Expand All @@ -31,6 +32,12 @@ public RegisterRequestMessageLdap(RegisterRequestMessage registerRequestMessage)
this.requestId = registerRequestMessage.getRequestId();
}

public RegisterRequestMessageLdap(String dn, String id, Date creationDate, String sessionState, String userName,
RegisterRequestMessage registerRequestMessage) {
super(dn, id, registerRequestMessage.getRequestId(), creationDate, sessionState, userName);
this.registerRequestMessage = registerRequestMessage;
}

public RegisterRequestMessage getRegisterRequestMessage() {
return registerRequestMessage;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,24 @@ public class RequestMessageLdap extends BaseEntry {
@LdapAttribute(name = "oxSessionStateId")
protected String sessionState;

@LdapAttribute(name = "uid")
protected String userName;

public RequestMessageLdap() {}

public RequestMessageLdap(String dn) {
super(dn);
}

public RequestMessageLdap(String dn, String id, String requestId, Date creationDate, String sessionState, String userName) {
super(dn);
this.id = id;
this.requestId = requestId;
this.creationDate = creationDate;
this.sessionState = sessionState;
this.userName = userName;
}

public String getId() {
return id;
}
Expand Down Expand Up @@ -66,4 +78,12 @@ public void setSessionState(String sessionState) {
this.sessionState = sessionState;
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

}
26 changes: 26 additions & 0 deletions Server/src/main/java/org/xdi/oxauth/service/CleanerTimer.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
import org.xdi.oxauth.model.common.AuthorizationGrant;
import org.xdi.oxauth.model.common.AuthorizationGrantList;
import org.xdi.oxauth.model.config.ConfigurationFactory;
import org.xdi.oxauth.model.fido.u2f.DeviceRegistration;
import org.xdi.oxauth.model.fido.u2f.RequestMessageLdap;
import org.xdi.oxauth.model.registration.Client;
import org.xdi.oxauth.service.fido.u2f.DeviceRegistrationService;
import org.xdi.oxauth.service.fido.u2f.RequestService;
import org.xdi.oxauth.service.uma.RPTManager;
import org.xdi.oxauth.service.uma.ResourceSetPermissionManager;
Expand Down Expand Up @@ -59,6 +61,9 @@ public class CleanerTimer {

@In
private MetricService metricService;

@In
private DeviceRegistrationService deviceRegistrationService;

private AtomicBoolean isActive;

Expand Down Expand Up @@ -96,6 +101,7 @@ public void process() {
this.resourceSetPermissionManager.cleanupResourceSetPermissions(now);

processU2fRequests();
processU2fDeviceRegistrations();

processMetricEntries();
} finally {
Expand Down Expand Up @@ -175,6 +181,26 @@ private void processU2fRequests() {
log.debug("End U2F request clean up");
}

private void processU2fDeviceRegistrations() {
log.debug("Start U2F request clean up");

Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
calendar.add(Calendar.SECOND, -90);
Date expirationDate = calendar.getTime();

List<DeviceRegistration> deviceRegistrations = deviceRegistrationService.getExpiredDeviceRegistrations(expirationDate);
if ((deviceRegistrations != null) && !deviceRegistrations.isEmpty()) {
for (DeviceRegistration deviceRegistration : deviceRegistrations) {
log.debug("Removing DeviceRegistration: {0}, Creation date: {1}",
deviceRegistration.getId(),
deviceRegistration.getCreationDate());
deviceRegistrationService.removeUserDeviceRegistration(deviceRegistration);
}
}

log.debug("End U2F request clean up");
}

private void processMetricEntries() {
log.debug("Start metric entries clean up");

Expand Down
18 changes: 16 additions & 2 deletions Server/src/main/java/org/xdi/oxauth/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ public boolean authenticate(String keyValue, String password, String primaryKey,
*
* @return User
*/
public User getUserByDn(String dn) {
return ldapEntryManager.find(User.class, dn);
public User getUserByDn(String dn, String... returnAttributes) {
return ldapEntryManager.find(User.class, dn, returnAttributes);
}

public User getUser(String userId, String... returnAttributes) {
Expand Down Expand Up @@ -128,6 +128,20 @@ public String getUserInum(String userId) {
return getUserInum(user);
}

public String getUserNameByInum(String inum) {
if (StringHelper.isEmpty(inum)) {
return null;
}

String userDn = getDnForUser(inum);
User user = getUserByDn(userDn, "uid");
if (user == null) {
return null;
}

return user.getUserId();
}

public User updateUser(User user) {
return ldapEntryManager.merge(user);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.jboss.seam.log.Log;
import org.xdi.oxauth.crypto.random.ChallengeGenerator;
import org.xdi.oxauth.exception.fido.u2f.DeviceCompromisedException;
import org.xdi.oxauth.exception.fido.u2f.InvalidKeyHandleDeviceException;
import org.xdi.oxauth.exception.fido.u2f.NoEligableDevicesException;
import org.xdi.oxauth.model.config.ConfigurationFactory;
import org.xdi.oxauth.model.fido.u2f.AuthenticateRequestMessageLdap;
Expand Down Expand Up @@ -183,23 +184,16 @@ public AuthenticateRequest getAuthenticateRequest(AuthenticateRequestMessage req
throw new BadInputException("Responses keyHandle does not match any contained request");
}

public void storeAuthenticationRequestMessage(AuthenticateRequestMessage requestMessage, String sessionState) {
public void storeAuthenticationRequestMessage(AuthenticateRequestMessage requestMessage, String userName, String sessionState) {
Date now = new GregorianCalendar(TimeZone.getTimeZone("UTC")).getTime();
final String authenticateRequestMessageId = UUID.randomUUID().toString();

AuthenticateRequestMessageLdap authenticateRequestMessageLdap = new AuthenticateRequestMessageLdap(requestMessage);
authenticateRequestMessageLdap.setId(authenticateRequestMessageId);
authenticateRequestMessageLdap.setDn(getDnForAuthenticateRequestMessage(authenticateRequestMessageId));
authenticateRequestMessageLdap.setCreationDate(now);
authenticateRequestMessageLdap.setSessionState(sessionState);
AuthenticateRequestMessageLdap authenticateRequestMessageLdap = new AuthenticateRequestMessageLdap(getDnForAuthenticateRequestMessage(authenticateRequestMessageId),
authenticateRequestMessageId, now, sessionState, userName, requestMessage);

ldapEntryManager.persist(authenticateRequestMessageLdap);
}

public void storeAuthenticationRequestMessage(AuthenticateRequestMessage requestMessage) {
storeAuthenticationRequestMessage(requestMessage, null);
}

public AuthenticateRequestMessage getAuthenticationRequestMessage(String oxId) {
String requestDn = getDnForAuthenticateRequestMessage(oxId);

Expand Down Expand Up @@ -228,10 +222,15 @@ public void removeAuthenticationRequestMessage(AuthenticateRequestMessageLdap au
removeRequestMessage(authenticateRequestMessageLdap);
}

public String getUserInumByKeyHandle(String appId, String keyHandle) {

public String getUserInumByKeyHandle(String appId, String keyHandle) throws InvalidKeyHandleDeviceException {
if (org.xdi.util.StringHelper.isEmpty(appId) || StringHelper.isEmpty(keyHandle)) {
return null;
}

List<DeviceRegistration> deviceRegistrations = deviceRegistrationService.findDeviceRegistrationsByKeyHandle(appId, keyHandle, "oxId");
if (deviceRegistrations.isEmpty()) {
throw new BadInputException(String.format("Failed to find device by keyHandle '%s' in LDAP", keyHandle));
throw new InvalidKeyHandleDeviceException(String.format("Failed to find device by keyHandle '%s' in LDAP", keyHandle));
}

if (deviceRegistrations.size() != 1) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

package org.xdi.oxauth.service.fido.u2f;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.gluu.site.ldap.persistence.LdapEntryManager;
Expand Down Expand Up @@ -83,6 +85,10 @@ public List<DeviceRegistration> findUserDeviceRegistrations(String userInum, Str
}

public List<DeviceRegistration> findDeviceRegistrationsByKeyHandle(String appId, String keyHandle, String ... returnAttributes) {
if (org.xdi.util.StringHelper.isEmpty(appId) || StringHelper.isEmpty(keyHandle)) {
return new ArrayList<DeviceRegistration>(0);
}

byte[] keyHandleDecoded = Base64Util.base64urldecode(keyHandle);

String baseDn = userService.getDnForUser(null);
Expand All @@ -97,12 +103,39 @@ public List<DeviceRegistration> findDeviceRegistrationsByKeyHandle(String appId,
return ldapEntryManager.findEntries(baseDn, DeviceRegistration.class, returnAttributes, filter);
}

public DeviceRegistration findOneStepUserDeviceRegistration(String deviceId, String... returnAttributes) {
String deviceDn = getDnForOneStepU2fDevice(deviceId);

return ldapEntryManager.find(DeviceRegistration.class, deviceDn);
}

public void addUserDeviceRegistration(String userInum, DeviceRegistration deviceRegistration) {
prepareBranch(userInum);

ldapEntryManager.persist(deviceRegistration);
}

public boolean attachUserDeviceRegistration(String userInum, String oneStepDeviceId) {
String oneStepDeviceDn = getDnForOneStepU2fDevice(oneStepDeviceId);

// Load temporary stored device registration
DeviceRegistration deviceRegistration = ldapEntryManager.find(DeviceRegistration.class, oneStepDeviceDn);
if (deviceRegistration == null) {
return false;
}

// Remove temporary stored device registration
removeUserDeviceRegistration(deviceRegistration);

// Attach user device registration to user
String deviceDn = getDnForU2fDevice(userInum, deviceRegistration.getId());

deviceRegistration.setDn(deviceDn);
addUserDeviceRegistration(userInum, deviceRegistration);

return true;
}

public void addOneStepDeviceRegistration(DeviceRegistration deviceRegistration) {
ldapEntryManager.persist(deviceRegistration);
}
Expand All @@ -119,6 +152,19 @@ public void disableUserDeviceRegistration(DeviceRegistration deviceRegistration)
ldapEntryManager.merge(deviceRegistration);
}

public void removeUserDeviceRegistration(DeviceRegistration deviceRegistration) {
ldapEntryManager.remove(deviceRegistration);
}

public List<DeviceRegistration> getExpiredDeviceRegistrations(Date expirationDate) {
final String u2fBaseDn = getDnForOneStepU2fDevice(null);
Filter expirationFilter = Filter.createLessOrEqualFilter("creationDate", ldapEntryManager.encodeGeneralizedTime(expirationDate));

List<DeviceRegistration> deviceRegistrations = ldapEntryManager.findEntries(u2fBaseDn, DeviceRegistration.class, expirationFilter);

return deviceRegistrations;
}

/**
* Build DN string for U2F user device
*/
Expand All @@ -138,7 +184,7 @@ public String getBaseDnForU2fUserDevices(String userInum) {
public String getDnForOneStepU2fDevice(String deviceRegistrationId) {
final String u2fBaseDn = ConfigurationFactory.instance().getBaseDn().getU2fBase(); // ou=registered_devices,ou=u2f,o=@!1111,o=gluu
if (StringHelper.isEmpty(deviceRegistrationId)) {
return String.format("ou=registrered_devices,%s", u2fBaseDn);
return String.format("ou=registered_devices,%s", u2fBaseDn);
}

return String.format("oxid=%s,ou=registered_devices,%s", deviceRegistrationId, u2fBaseDn);
Expand Down
Loading

0 comments on commit d1e3021

Please sign in to comment.