Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
/**
 * 
 * Login using BankID SE
 * 
 * All texts, flows and documentation is available at
 * 
 * https://www.bankid.com/assets/bankid/rp/bankid-relying-party-guidelines-v2.15.pdf
 *  
 * @author Kim Rasmussen
 * @version $Revision$
 *
 * <pre>
 * Ceptor - http://ceptor.io
 * Copyright(c) 2017, Asseco Denmark A/S, All rights reserved.
 * 
 * This source code is confidential.
 * </pre>
 */
public class SwedishBankIDAuth extends VerticalLayout {
	private static final long serialVersionUID = 1L;
	private IController controller;
	private TextField personNummer;
	private Button next;
	private Button thisComputer;
	private Button mobileDevice;
	private Refresher refresher;
	private boolean isIdProvided = false;
	
	private boolean isMobileDevice = DeviceUtils.isMobileDevice();
	
	public SwedishBankIDAuth(IController controller) {
		this.controller = controller;
		
		setStyleName("small-padding");
		setWidth("100%");
		setMargin(false);
		
		populateInitialGUI();
	}

	private void populateInitialGUI() {
		removeAllComponents();
		// Differ messages depending on running in browser or on mobile
		String promptKey = isMobileDevice ? "RFA20" : "RFA19";
		addComponent(new Label(Language.getText(promptKey, promptKey)));
		
		thisComputer = new Button(Language.getText(promptKey+"_THIS", promptKey+"_THIS"), e -> useThisComputer());
		thisComputer.setStyleName("custom");
		
		mobileDevice = new Button(Language.getText(promptKey+"_MOBILE", promptKey+"_MOBILE"), e -> useMobileDevice());
		thisComputer.setStyleName("custom");
		
		HorizontalLayout hl = new HorizontalLayout();
		hl.addComponents(thisComputer, mobileDevice);
		addComponent(hl);
		
		// Start with clean slate
		try {
			Agent.getInstance().setStateVariable(JSPHelper.getSessionID(), "bankidse_orderRef", null, false);
			Agent.getInstance().setStateVariable(JSPHelper.getSessionID(), "bankidse_autoStartToken", null, false);
		} catch(PTException e) {
			// OK to ignore
		}
	}
	
	private void useThisComputer() {
		isIdProvided = false;
		
		// No ID needed, since we are using the same device
		try {
			// Credentials here ensure that only BankID mobile can be used.
			
			Agent.getInstance().logon(JSPHelper.getSessionID(), AuthTypes.AUTHTYPE_BANKID_SE, "", "{\"certificatePolicies\": [\"1.2.752.78.1.5\"]}"); // TODO: Need to consider test environment vs production
		} catch(PTException e) {
			if (e.getErrorCode() != AuthErrorCodes.ERROR_NOT_YET_COMPLETED) { // Error 19 means that authentication is started
				handleError(e);
				return;
			}
		}
		
		redirectNow();
	}

	private void redirectNow() {
		// If we get here, we can redirect
		try {
			String autoStartToken = Agent.getInstance().getStateVariable(JSPHelper.getSessionID(), "bankidse_autoStartToken");

			String challenge = Base64.encode(UUID.randomUUID().toString());
			Agent.getInstance().setStateVariable(JSPHelper.getSessionID(), "challenge", challenge, false);
			
			// Redirect to BankID on the same computer/device
			String url = "bankid:///?autostarttoken="+URLUtils.encodeURL(autoStartToken)+"&rpref="+URLUtils.encodeURL(challenge)+"&redirect="+URLUtils.encodeURL(Page.getCurrent().getLocation().toString());
			
			// When we get back, we need to poll immediately to see if it worked.
			initiatePoll();
			Page.getCurrent().setLocation(url);
		} catch (PTException e) {
			handleError(e);
		}
	}
	
	private void handleError(PTException e) {
		removeAllComponents();
		
		if (refresher != null) {
			removeExtension(refresher);
			refresher = null;
		}
		Label l = new Label(getMessage(e));
		l.addStyleName(ValoTheme.LABEL_FAILURE);
		addComponent(l);
		addComponent(new Button(Language.getText("bankid.retry", "Try again"), (source) -> populateInitialGUI()));
	}
	
	private void initiatePoll() {
		// Start the polling...
		refresher = new Refresher();
		refresher.setRefreshInterval(2000);
		addExtension(refresher);
		refresher.addListener(source -> pollStatus());
	}
	
	private void pollStatus() {
		try {
			// Credentials here ensure that only BankID mobile can be used.
			
			Agent.getInstance().logon(JSPHelper.getSessionID(), AuthTypes.AUTHTYPE_BANKID_SE, "", "");
			
			if (refresher != null) {
				removeExtension(refresher);
				refresher = null;
			}
			// It worked, login is done.
			controller.bankIDLoginCompleted();
			return;
		} catch(PTException e) {
			if (e.getErrorCode() != AuthErrorCodes.ERROR_NOT_YET_COMPLETED) { // Error 19 means that authentication is started
				handleError(e);
				return;
			}
			
			removeAllComponents();
			
			addComponent(new Label(getMessage(e)));
		}
		
		
	}
	
	private String getMessage(PTException e) {
		String progressStatus = e.getErrorText();
		if (progressStatus == null)
			return e.toString();
		String message = progressStatus;
		
		switch(progressStatus) {
		case "OUTSTANDING_TRANSACTION":
		case "NO_CLIENT":
			message = Language.getText("RFA1", "RFA1");
			break;
		case "ALREADY_IN_PROGRESS":
		case "CANCELLED":
			message = Language.getText("RFA3", "RFA3");
			break;
		case "RETRY":
		case "INTERNAL_ERROR":
			message = Language.getText("RFA5", "RFA5");
			break;
		case "USER_CANCEL":
			message = Language.getText("RFA6", "RFA6");
			break;
		case "EXPIRED_TRANSACTION":
			message = Language.getText("RFA8", "RFA8");
			break;
		case "USER_SIGN":
			message = Language.getText("RFA9", "RFA9");
			break;
		case "CLIENT_ERR":
			message = Language.getText("RFA12", "RFA12");
			break;
		case "STARTED":
			if (isMobileDevice) {
				if (isIdProvided)
					message = Language.getText("RFA14_B", "RFA14_B");
				else
					message = Language.getText("RFA15_B", "RFA15_B");
			} else { // PC is used
				if (isIdProvided)
					message = Language.getText("RFA14_A", "RFA14_A");
				else
					message = Language.getText("RFA15_A", "RFA15_A");
			}
			break;
		case "START_FAILED":
			message = Language.getText("RFA17", "RFA17");
			break;
		}
		
		return message;
	}
	/**
	 * Use mobile (or other device if already running on mobile)
	 */
	private void useMobileDevice() {
		// Prompt for ID
		setupPersonnummer();
	}
	
	private void setupPersonnummer() {
		removeAllComponents();
		
		personNummer = new TextField();
		personNummer.setPlaceholder("ID");
		personNummer.setIcon(VaadinIcons.USER);
		personNummer.setStyleName(ValoTheme.TEXTFIELD_INLINE_ICON);
		personNummer.setWidth("100%");
		addComponent(personNummer);
		
		next = new Button(Language.getText("RFA18", "RFA18"), e -> nextBtnClickListener());
		next.setStyleName("custom");
		addComponent(next);
		
		setComponentAlignment(next, Alignment.BOTTOM_RIGHT);
	}

	private void nextBtnClickListener() {
		if (personNummer.isEmpty()) {
			Notification.show(Language.getText("bankid.emptyid", "Please enter ID"));
			personNummer.focus();
			return;
		}
		
		isIdProvided = true;
		try {
			Agent.getInstance().logon(JSPHelper.getSessionID(), AuthTypes.AUTHTYPE_BANKID_SE, personNummer.getValue().trim(), "");
			controller.bankIDLoginCompleted();
		} catch(PTException e) {
			if (e.getErrorCode() != AuthErrorCodes.ERROR_NOT_YET_COMPLETED) { // Error 19 means that authentication is started
				handleError(e);
				return;
			}
		}
		
		removeAllComponents();
		addComponent(new Label(Language.getText("RFA1", "RFA1")));
		
		initiatePoll();
	}
}

...