Versions Compared

Key

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

...

index.html
css/checkforce-stylke.css
css/materialize.min.css
icon/favicon.png
js/ceptor.js
js/axios.min.js
js/checkforce.min.js
js/jquery-3.4.1.min.js
js/materialize.min.jss

...

Code Block
languagejs
titleceptor.js
linenumberstrue
collapsetrue
/**
 * Ceptor login / reset password user interface
 * 
 * Uses CeptorAuthenticate API in the Gateway - check /ceptorauthenticate/1/?openapi.json for details
 * 
 */
var locationName;
var productName;
var pathPrefix = '/ceptorauthenticate/1';
var availableOTPMethods = ['totp','sms','email'];
var selectedOTP;

var Ceptor = {};

/**
 * Initialize Ceptor
 * 
 * @param loc Location to draw content in
 * @param prod Product name to display on login page
 */
Ceptor.init = function(loc, prod) {
    locationName = loc;
    productName = prod;
    document.Ceptor = this;
}

/**
 * Show login page, but first check status - if already authenticated, instead jump to afterLoginPage()
 */
Ceptor.loginPage = function() {
    axios.get(pathPrefix+'/info')
    .then(function (response) {
        console.log(response);
        // handle success
        if (response.data.logged_on) {
            document.Ceptor.afterLoginPage(response.data);
        } else {
            Ceptor.showActualLoginPage();
        }
    })
    .catch(function (error) {
        console.log(error);
        document.Ceptor.handleError("Unable to check Login status", error);
        document.Ceptor.showActualLoginPage();
    });
}

/**
 * Show login page prompting for userid and password
 */
Ceptor.showActualLoginPage = function() {
    $(locationName).html(
        '<div id="login-page" class="row">'+
        '    <div class="card">'+
        '        <div class="card-action blue white-text">'+
        '            <h3 class="center">Please Authenticate</h3>'+
        '           <div class="black-text center">'+
        '              <h5>Thank you for choosing '+$('<div>').text(productName).html()+'</h5>'+
        '            </div>'+
        '        </div>'+
        '    </div><br>'+
        '    <div class="card-content">'+
        '        <div class="input-field">'+
        '            <i class="material-icons prefix">person</i>'+
        '            <input class="validate" id="userid" type="text">'+
        '            <label for="userid">User ID</label>'+
        '        </div>'+
        '        <div class="input-field">'+
        '            <i class="material-icons prefix">lock_outline</i>'+
        '            <input id="password" type="password">'+
        '            <label for="password">Password</label>'+
        '        </div><br>'+
        '        <div class="form-field">'+
        '            <button id="loginbutton" class="btn-large waves-effect waves-light blue" style="width:100%" onclick="document.Ceptor.loginClicked()">Login</button>'+
        '        </div>'+
        '        <div class="input-field col s6 m6 l6">'+
        '            <p class="margin medium-small"><a href="#register" onclick="document.Ceptor.registerPage()">Register Now!</a></p>'+
        '        </div>'+
        '        <div class="input-field col s6 m6 l6">'+
        '            <p class="margin right-align medium-small"><a href="#reset" onclick="document.Ceptor.resetPasswordPage()">Forgot password?</a></p>'+
        '        </div>'+
        '        <br>'+
        '        <br>'+
        '    </div>'+
        '</div>'
    );

    $('#userid').focus();

    $('#userid').keyup(function() {
        if (event.keyCode === 13) {
            $('#password').focus();
        }
    });
    $('#password').keyup(function() {
        if (event.keyCode === 13) {
            $('#loginbutton').click();
        }
    });
}

/**
 * Handles logon part 1, of userid and password fields.
 */
Ceptor.loginClicked = function() {
    var userid = $('#userid').val();
    var password = $('#password').val();

    $('#loginbutton').prop( "disabled", true );

    axios.post(pathPrefix+'/auth/uidpw', {
        userid: userid,
        credentials: password
    })
    .then(function (response) {
        // handle success
        Ceptor.afterLoginPage(response.data);
    })
    .catch(function (error) {
        console.log(error.response.data);
        if (error.response.data.code == 15) { // ERROR_NEED_OTP
            availableOTPMethods = error.response.data.otpmethods;
            // Prompt for OTP
            Ceptor.loginPageOTP();
        } else {
            Ceptor.handleError("Unable to login", error);
        }
    })
    .finally(function () {
        $('#loginbutton').prop( "disabled", false );
    });
}

/**
 * Shows 2nd part of login page, prompting for OTP code
 */
Ceptor.loginPageOTP = function() {
    var str=
        '<div id="login-page" class="row">'+
        '    <div class="card">'+
        '        <div class="card-action blue white-text">'+
        '            <h3 class="center">Please Authenticate</h3>'+
        '           <div class="black-text center">'+
        '              <h5>Multifactor Login required</h5>'+
        '            </div>'+
        '        </div>'+
        '    </div><br>'+
        '    <div class="card-content">'+
        '    <h5>Select method for One-Time-Pin verification</h5><br>'+
        '    <div class="col s12">'+
        '        <ul class="tabs">';

    if (availableOTPMethods.includes("totp"))
        str +='            <li class="tab col s3"><a href="#page1" onclick="selectedOTP=\'totp\';">Authenticator</a></li>';
    if (availableOTPMethods.includes("sms"))
        str +='            <li class="tab col s3"><a href="#page2" onclick="selectedOTP=\'sms\';">SMS / Text Message</a></li>';
    if (availableOTPMethods.includes("email"))
        str +='            <li class="tab col s3"><a href="#page3" onclick="selectedOTP=\'email\';">Email</a></li>';

    str+='        </ul>'+
        '    </div>';

    if (availableOTPMethods.includes("totp"))
        str+='    <div id="page1" class="col s12">'+
            '        <div class="input-field">'+
            '            <i class="material-icons prefix">person</i>'+
            '            <input class="validate" id="totp" type="text">'+
            '            <label for="totp">Enter TOTP code from Authenticator App</label>'+
            '        </div>'+
            '    </div>';

    if (availableOTPMethods.includes("sms"))
        str+='    <div id="page2" class="col s12">'+
            '        <div class="input-field" id="sms_otp_container">'+
            '            <button id="sendsmsbutton" class="btn-small green" onclick="Ceptor.sendSMS()">Send SMS</button>'+
            '        </div>'+
            '        <div class="input-field" id="sms_input_container" hidden>'+
            '            <i class="material-icons prefix">person</i>'+
            '            <input class="validate" id="sms" type="text">'+
            '            <label for="totp">Enter code sent to your mobile phone</label>'+
            '        </div>'+
            '    </div>';

    if (availableOTPMethods.includes("email"))
        str+='    <div id="page3" class="col s12">'+
            '        <div class="input-field" id="email_otp_container">'+
            '            <button id="sendemailbutton" class="btn-small green" onclick="Ceptor.sendEmailOTP()">Send email with code</button>'+
            '        </div>'+
            '        <div class="input-field" id="email_input_container" hidden>'+
            '            <i class="material-icons prefix">person</i>'+
            '            <input class="validate" id="emailotp" type="text">'+
            '            <label for="totp">Enter code sent to your email</label>'+
            '        </div>'+
            '    </div>';

    str+='</div>'+
        '<div class="form-field">'+
        '    <button id="loginbutton" class="btn-large waves-effect waves-light blue" onclick="document.Ceptor.loginOTPClicked()" style="width:100%">Verify</button>'+
        '</div>'+
        '<div class="input-field col s6 m6 l6">'+
        '    <p class="margin medium-small"><a href="#" onclick="document.Ceptor.loginPage()">Back</a></p>'+
        '</div>'+
        '</div>';

    $(locationName).html(str);

    M.Tabs.init($('.tabs'), {
        duration: 300,
        swipeable: false
    });

    selectedOTP = availableOTPMethods[0];
    $('#totp#'+selectedOTP).focus();
}

/**
 * Sends an SMS with an OTP code to the client
 */
Ceptor.sendSMS = function() {
    $('#sendsmsbutton').prop( "disabled", true );
    $('#sms_otp_container').fadeOut({duration:100});

    axios.put(pathPrefix+'/auth/sendsms')
    .then(function (response) {
        // handle success

        // Show prompt
        $('#sms_input_container').show({
            duration:400,
            complete: function() {$('#sms').focus();}
        });
    })
    .catch(function (error) {
        $('#sms_otp_container').fadeIn({duration:200});
        Ceptor.handleError("Unable to send SMS/Text message", error);
    })
    .finally(function () {
        $('#sendsmsbutton').prop( "disabled", false );
    });
}

/**
 * Sends an email with an OTP code to the client
 */
Ceptor.sendEmailOTP = function() {
    $('#login-page').prop( "disabled", true );
    $('#email_otp_container').fadeOut({duration:100});

    axios.put(pathPrefix+'/auth/sendemail')
    .then(function (response) {
        // handle success

        // Show prompt
        $('#email_input_container').show({
            duration:400,
            complete: function() {$('#emailotp').focus();}
        });
    })
    .catch(function (error) {
        $('#email_otp_container').fadeIn({duration:200});
        Ceptor.handleError("Unable to send SMS/Text message", error);
    })
    .finally(function () {
        $('#login-page').prop( "disabled", false );
    });
}

/**
 * Handles logon part 2, authenticating user using the selected OTP method
 */
Ceptor.loginOTPClicked = function() {
    if (selectedOTP === 'sms') {
        var otp = $('#sms').val();

        if (!otp) {
             $('#sms').focus();
             return;
        }

        $('#loginbutton').prop( "disabled", true );

        axios.post(pathPrefix+'/auth/verifysmsotp?code='+otp)
        .then(function (response) {
            // handle success - the login page will fetch /info and go to the confirm page
            Ceptor.loginPage();
        })
        .catch(function (error) {        
            Ceptor.handleError("Unable to verify entered code", error);
        })
        .finally(function () {
            $('#loginbutton').prop( "disabled", false );
        });
    } else if (selectedOTP === 'totp') {
        var otp = $('#totp').val();

        if (!otp) {
             $('#totp').focus();
             return;
        }

        $('#loginbutton').prop( "disabled", true );

        axios.post(pathPrefix+'/auth/verifytotp?code='+otp)
        .then(function (response) {
            // handle success - the login page will fetch /info and go to the confirm page
            Ceptor.loginPage();
        })
        .catch(function (error) {        
            Ceptor.handleError("Unable to verify entered code", error);
        })
        .finally(function () {
            $('#loginbutton').prop( "disabled", false );
        });
    } else if (selectedOTP === 'email') {
        var otp = $('#emailotp').val();

        if (!otp) {
             $('#emailotp').focus();
             return;
        }

        $('#loginbutton').prop( "disabled", true );

        axios.post(pathPrefix+'/auth/verifyemailotp?code='+otp)
        .then(function (response) {
            // handle success - the login page will fetch /info and go to the confirm page
            Ceptor.loginPage();
        })
        .catch(function (error) {        
            Ceptor.handleError("Unable to verify entered code", error);
        })
        .finally(function () {
            $('#loginbutton').prop( "disabled", false );
        });
    }
}


/**
 * Page shown after login
 * 
 * @param info Session info from /info API call
 */
Ceptor.afterLoginPage = function(info) {
    $(locationName).html(
        '<div id="login-result" class="row">'+
        '    <div class="card">'+
        '        <div class="card-action blue white-text">'+
        '            <h3 class="center">Welcome</h3>'+
        '           <div class="black-text center">'+
        '              <h5>Hello '+$('<div>').text(info.user_name).html()+'</h5>'+
        '           </div>'+
        '        </div>'+
        '    </div>'+
        '</div>'+
        '<div class="card-panel">'+
        '     <span class="blue-text text-darken-2"><h6>Please go to <a href="https://ceptor.io">ceptor.io</a> for more information about Ceptor</h6></span>'+
        '</div>'+
        '<div class="fixed-action-btn">'+
        '    <a class="btn-floating btn-large red pulse">'+
        '        <i class="large material-icons">menu</i>'+
        '    </a>'+
        '    <ul>'+
        '        <li><a class="btn-floating yellow darken-1blue" onclick="document.Ceptor.logoffregisterTOTPPage()"><i class="material-icons">exit_to_app<>person</i></a></li>'+
//        '        <li><a class="btn-floating blueyellow darken-1" onclick="document.Ceptor.logoff()"><i class="material-icons">person<>exit_to_app</i></a></li>'+
        '    </ul>'+
        '</div>'
    );

    var elems = document.querySelectorAll('.fixed-action-btn');
    M.FloatingActionButton.init(elems, {});
}

/**
 * Handles an error received by the server, displaying error message
 * 
 * @param message Prefix message, e.g. "Login error" - error message from server is appended to it
 * @param error The error returned from axios client 
 */
Ceptor.handleError = function(message, error) {
    console.log(error.response);
    if (error.response.status === 401 || error.response.status === 400) {
        M.toast({html: '<b>'+message+': </b> ' + $("<div>").text(error.response.data.message).html()});
    } else {
        M.toast({html: '<b>'+message+': </b> ' + $("<div>").text(error).html()});
        console.log(error.response.data);
    }
}

/**
 * Show the reset password page
 */
Ceptor.resetPasswordPage = function() {
    $(locationName).html(
        '<div id="reset-page" class="row">'+
        '    <div class="card">'+
        '        <div class="card-action blue white-text">'+
        '            <h3 class="center">Forgot Password?</h3>'+
        '            <div class="black-text center">'+
        '                <h5>Reset your password here</h5>'+
        '            </div>'+
        '        </div>'+
        '    </div><br>'+
        '    <div id="resetcontent" class="card-content">'+
        '        <div class="input-field">'+
        '            <i class="material-icons prefix">person</i>'+
        '            <input class="validate" id="user" type="text">'+
        '            <label for="user">User ID</label>'+
        '        </div>'+
        '        <div class="input-field">'+
        '            <i class="material-icons prefix">mail_outline</i>'+
        '            <input class="validate" id="email" type="email">'+
        '            <label for="email">Email</label>'+
        '            <span class="helper-text" data-error="Not an email address" data-success="">Please enter email address</span>'+
        '        </div>'+
        '  <div id="part2"><br>'+
        '        <div class="form-field">'+
        '            <button class="btn-large waves-effect waves-light blue" onclick="document.Ceptor.resetPasswordPage2()" style="width:100%">Continue</button>'+
        '        </div>'+
        '        <div class="input-field col s6 m6 l6">'+
        '            <p class="margin medium-small"><a href="#" onclick="document.Ceptor.loginPage()">Back</a></p>'+
        '        </div>'+
        '    </div>'+
        '  </div>'+
        '</div>');

        $('#user').focus();
}

/**
 * Shows reset password page 2
 * 
 * Validate userid and email address are entered, if so, prompt for OTP code, allowing choosing which OTP method to use
 */
Ceptor.resetPasswordPage2 = function() {
    // Validate fields first

    var userid = $("#user").val();
    var email =  $("#email").val();

    if (!userid) {
        $("#user").focus();
        M.toast({html: "Please fill in userid"});
        return;
    }
    if (!email || !validateEmail(email)) {
        $("#email").focus();
        M.toast({html: "Please fill in a valid email address"});
        return;
    }

    $("#user").prop( "disabled", true );
    $("#email").prop( "disabled", true );

   var str =
        '<h5>Select method for One-Time-Pin verification</h5><br>'+
        '<div class="row">'+
        '    <div class="col s12">'+
        '        <ul class="tabs">';

    if (availableOTPMethods.includes("totp"))
        str +='            <li class="tab col s3"><a href="#page1" onclick="selectedOTP=\'totp\';">Authenticator</a></li>';
    if (availableOTPMethods.includes("sms"))
        str +='            <li class="tab col s3"><a href="#page2" onclick="selectedOTP=\'sms\';">SMS / Text Message</a></li>';
    if (availableOTPMethods.includes("email"))
        str += '            <li class="tab col s3"><a href="#page3" onclick="selectedOTP=\'email\';">Email</a></li>';

    str +=
        '        </ul>'+
        '    </div>';

    if (availableOTPMethods.includes("totp"))
        str+='    <div id="page1" class="col s12">'+
            '        <div class="input-field">'+
            '            <i class="material-icons prefix">person</i>'+
            '            <input class="validate" id="totp" type="text">'+
            '            <label for="totp">Enter TOTP code</label>'+
            '        </div>'+
            '    </div>';

    if (availableOTPMethods.includes("sms"))
        str+='    <div id="page2" class="col s12">'+
            '        <div class="input-field" id="sms_otp_container">'+
            '            <button id="sendsmsbutton" class="btn-small green" onclick="Ceptor.resetSMSClicked()">Send SMS</button>'+
            '        </div>'+
            '        <div class="input-field" id="sms_input_container" hidden>'+
            '            <i class="material-icons prefix">person</i>'+
            '            <input class="validate" id="sms" type="text">'+
            '            <label for="totp">Enter code sent to your mobile phone</label>'+
            '        </div>'+
            '    </div>';

    if (availableOTPMethods.includes("email"))
        str+='    <div id="page3" class="col s12">'+
            '        <div class="input-field" id="email_otp_container">'+
            '            <button id="sendemailbutton" class="btn-small green" onclick="Ceptor.resetEmailClicked()">Send email with code</button>'+
            '        </div>'+
            '        <div class="input-field" id="email_input_container" hidden>'+
            '            <i class="material-icons prefix">person</i>'+
            '            <input class="validate" id="emailotp" type="text">'+
            '            <label for="totp">Enter code sent to your email</label>'+
            '        </div>'+
            '    </div>';


    str+='</div>'+
        '<div class="form-field">'+
        '    <button class="btn-large waves-effect waves-light blue" onclick="document.Ceptor.resetPasswordPage3()" style="width:100%">Verify</button>'+
        '</div>'+
        '<div class="input-field col s6 m6 l6">'+
        '    <p class="margin medium-small"><a href="#" onclick="document.Ceptor.loginPage()">Back</a></p>'+
        '</div>'+
        '';

        $("#part2").html(str);

        $("#part2").prop("class", "card-panel indigo lighten-5");
    M.Tabs.init($('.tabs'), {
        duration: 300,
        swipeable: false
    });

    selectedOTP = availableOTPMethods[0];    
    $('#totp').focus();
}

/**
 * Reset password page 3, after entering userid, email and OTP code, asks for new password if codes are verified
 * 
 * Validate entered OTP code, if correct then ask for new password
 */
Ceptor.resetPasswordPage3 = function() {
    var userid = $("#user").val();
    var email =  $("#email").val();
    var enteredOTP;

    if (selectedOTP === 'sms') {
        enteredOTP = $('#sms').val();
        if (!enteredOTP) {
            $('#sms').focus();
            M.toast({html: "Please enter OTP"});
            return;
        }
    } else if (selectedOTP === 'email') {
        enteredOTP = $('#emailotp').val();
        if (!enteredOTP) {
            $('#emailotp').focus();
            M.toast({html: "Please enter OTP"});
            return;
        }
    } else if (selectedOTP === 'totp') {
        enteredOTP = $('#totp').val();
        if (!enteredOTP) {
            $('#totp').focus();
            M.toast({html: "Please enter OTP"});
            return;
        }
    }

    axios.post(pathPrefix+'/resetpasswordotp', {
        userid: userid,
        email: email,
        otptype: selectedOTP,
        code: enteredOTP,
    })
    .then(function (response) {
        // handle success

        $("#part2").html(
            '<h5>Enter new password</h5><br>'+
            '        <div class="input-field">'+
            '            <i class="material-icons prefix">lock_outline</i>'+
            '            <input id="password1" type="password">'+
            '            <label for="password1">Password</label>'+
            '        </div>'+
            '        <div class="pwstrength_viewport_progress"></div>'+
            '        <div class="input-field">'+
            '            <i class="material-icons prefix">lock_outline</i>'+
            '            <input id="password2" type="password">'+
            '            <label for="password2">Verify Password</label>'+
            '        </div><br>'+
            '        <div class="form-field">'+
            '            <button id="setpasswordbutton" class="btn-large waves-effect waves-light blue" onclick="document.Ceptor.resetPasswordPage4()" style="width:100%">Set new password</button>'+
            '        </div>'+
            '        <div class="input-field col s6 m6 l6">'+
            '            <p class="margin medium-small"><a href="#" onclick="document.Ceptor.loginPage()">Back</a></p>'+
            '        </div>'+
            '');

        var render = document.querySelector('.pwstrength_viewport_progress');

        CheckForce('#password1',{
            MaterializeTheme:true
        }).checkPassword(function(response){
            render.innerHTML = response.content;
        });
    })
    .catch(function (error) {
        Ceptor.handleError("Unable to reset password", error);
    });
}

Ceptor.resetSMSClicked = function() {
    var userid = $("#user").val();
    var email =  $("#email").val();
 
    $('#sendsmsbutton').prop( "disabled", true );
    $('#sms_otp_container').fadeOut({duration:100});

    axios.post(pathPrefix+'/initiateresetpassword', {
        userid: userid,
        email: email,
        otptype: 'sms',
    })
    .then(function (response) {
        // handle success

        // Show prompt
        $('#sms_input_container').show({
            duration:400,
            complete: function() {$('#sms').focus();}
        });
    })
    .catch(function (error) {
        $('#sms_otp_container').fadeIn({duration:200});
        Ceptor.handleError("Unable to send SMS/Text message", error);
    })
    .finally(function () {
        $('#sendsmsbutton').prop( "disabled", false );
    });
}

Ceptor.resetEmailClicked = function() {
    var userid = $("#user").val();
    var email =  $("#email").val();
 
    $('#sendemailbutton').prop( "disabled", true );
    $('#email_otp_container').fadeOut({duration:100});

    axios.post(pathPrefix+'/initiateresetpassword', {
        userid: userid,
        email: email,
        otptype: 'email',
    })
    .then(function (response) {
        // handle success

        // Show prompt
        $('#email_input_container').show({
            duration:400,
            complete: function() {$('#emailotp').focus();}
        });
    })
    .catch(function (error) {
        $('#email_otp_container').fadeIn({duration:200});
        Ceptor.handleError("Unable to send SMS/Text message", error);
    })
    .finally(function () {
        $('#sendemailbutton').prop( "disabled", false );
    });
}

/**
 * Last reset password page - validates passwords entered, then submits the password reset request to the server
 */
Ceptor.resetPasswordPage4 = function() {
    // Validate both fields first

    var userid = $("#user").val();
    var password1 = $("#password1").val();
    var password2 = $("#password2").val();

    if (!password1) {
        $("#password1").focus();
        M.toast({html: "Please enter a password"});
        return;
    }
    if (!password2) {
        $("#password2").focus();
        M.toast({html: "Please verify password"});
        return;
    }
    if (password1 != password2) {
        $("#password2").focus();
        M.toast({html: "The passwords must match"});
        return;
    }

    $('#setpasswordbutton').prop( "disabled", true );

    axios.post(pathPrefix+'/resetpassword', {
        userid: userid,
        newpassword: password1
    })
    .then(function (response) {
        // handle success

        M.toast({html: "Password reset successfully - please login."});
        Ceptor.loginPage();
    })
    .catch(function (error) {
        Ceptor.handleError("Unable to reset password", error);
    })
    .finally(function () {
        $('#setpasswordbutton').prop( "disabled", false );
    });
}

/**
 * Displays registration page, prompting for user info
 */
Ceptor.registerPage = function() {
    $(locationName).html(
        '<div id="register-page" class="row">'+
        '    <div class="card">'+
        '        <div class="card-action blue white-text">'+
        '            <h3 class="center">Register</h3>'+
        '            <div class="black-text center">'+
        '                <h5>Sign up here</h5>'+
        '            </div>'+
        '        </div>'+
        '    </div><br>'+
        '    <div class="col s12">'+
        '        <div class="row">'+
        '            <div class="input-field col s6">'+
        '                <i class="material-icons prefix">edit</i>'+
        '                <input class="validate" id="firstname" type="text">'+
        '                <label for="firstname">First Name</label>'+
        '            </div>'+
        '            <div class="input-field col s6">'+
        '                <input class="validate" id="lastname" type="text">'+
        '                <label for="lastname">Last Name</label>'+
        '            </div>'+
        '        </div>'+
        '        <div class="row">'+
        '            <div class="input-field col s6">'+
        '                <i class="material-icons prefix">mail_outline</i>'+
        '                <input class="validate" id="email" type="email">'+
        '                <label for="email">Email</label>'+
        '                <span class="helper-text" data-error="Not a valid email address" data-success=""></span>'+
        '            </div>'+
        '            <div class="input-field col s6">'+
        '                <i class="material-icons prefix">phone</i>'+
        '                <input class="validate" id="mobile" type="text">'+
        '                <label for="email">Mobile</label>'+
        '            </div>'+
        '        </div>'+
        '        <div class="row">'+
        '            <div class="input-field col s12">'+
        '                <i class="material-icons prefix">person</i>'+
        '                <input class="validate" id="userid" type="text">'+
        '                <label for="userid">User ID</label>'+
        '            </div>'+
        '        </div>'+
        '        <div class="row">'+
        '           <div class="input-field col s12">'+
        '               <i class="material-icons prefix">lock_outline</i>'+
        '               <input id="password1" type="password">'+
        '               <label for="password1">Password</label>'+
        '               <div class="pwstrength_viewport_progress"></div>'+
        '           </div>'+
        '        </div>'+
        '        <div class="row">'+
        '           <div class="input-field col s12">'+
        '               <i class="material-icons prefix">lock_outline</i>'+
        '               <input id="password2" type="password">'+
        '               <label for="password2">Verify Password</label>'+
        '           </div>'+
        '        </div>'+
        '        <div class="form-field">'+
        '            <button class="btn-large waves-effect waves-light blue" style="width:100%" onclick="Ceptor.registerClicked()">Register</button>'+
        '        </div>'+
        '        <div class="input-field col s6 m6 l6">'+
        '            <p class="margin medium-small"><a href="#" onclick="document.Ceptor.loginPage()">Back</a></p>'+
        '        </div>'+
        '    </div>'+
        '</div>');

    var render = document.querySelector('.pwstrength_viewport_progress');

    CheckForce('#password1',{
        MaterializeTheme:true
    }).checkPassword(function(response){
        render.innerHTML = response.content;
    });

    $('#firstname').focus();
}

Ceptor.registerClicked = function() {
    var firstname = $("#firstname").val();
    var lastname = $("#lastname").val();
    var email =  $("#email").val();
    var mobile =  $("#mobile").val();
    var userid =  $("#userid").val();
    var password1 = $("#password1").val();
    var password2 = $("#password2").val();

    if (!firstname) {
        $('#firstname').focus();
        M.toast({html: "Please enter first name"});
        return;
    }
    if (!lastname) {
        $('#lastname').focus();
        M.toast({html: "Please enter last name"});
        return;
    }
    if (!email || !validateEmail(email)) {
        $('#email').focus();
        M.toast({html: "Please enter a valid email address"});
        return;
    }
    if (!mobile) {
        $('#mobile').focus();
        M.toast({html: "Please enter a valid mobile number"});
        return;
    }
    if (!userid) {
        $('#userid').focus();
        M.toast({html: "Please enter userid"});
        return;
    }

    if (!password1) {
        $("#password1").focus();
        M.toast({html: "Please enter a password"});
        return;
    }
    if (!password2) {
        $("#password2").focus();
        M.toast({html: "Please verify password"});
        return;
    }
    if (password1 != password2) {
        $("#password2").focus();
        M.toast({html: "The passwords must match"});
        return;
    }

    axios.post(pathPrefix+'/register', {
        firstname: firstname,
        lastname: lastname,
        email: email,
        mobile: mobile,
        userid: userid,
        password: password1
    })
    .then(function (response) {
        // handle success

        M.toast({html: "Registration successful"});
        Ceptor.loginPage();
    })
    .catch(function (error) {
        Ceptor.handleError("Unable to reset password", error);
    });

}

/**
 * Validates email address
 * 
 * @param {email} email 
 */
function validateEmail(email) {
  var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
  return re.test(email);
}

/**
 * Calls logoff, and goes back to login page
 */
Ceptor.logoff = function() {
    axios.put(pathPrefix+'/auth/logoff')
    .then(function (response) {
        // handle success
        Ceptor.showActualLoginPage();
        M.toast({html: "Logged off"});
    })
    .catch(function (error) {
        console.log(error);
        Ceptor.handleError("Unable to logoff", error);
        Ceptor.loginPage();
    });
}


Ceptor Gateway Location

To serve the application to the clients, on /auth/* you need to create a new Location in the gateway, from which the static files in the applications are served to the user.

Create a location called "auth", and section the action to "serve".

Image Removed

Set conditions to match /auth/*

Image Removed

and create an URL rewrite rule, which rewrites /auth/(.*) to /$1 - effectively stripping the /auth/ prefix from the request.

Image Removed

Select the WebServer section, and point the webserver at ${ceptor.home}/auth where the html / javascript application is then served from.

Image Removed

Below is a location configuration which can be copied into the gateway configuration, with the configuration described above.

Code Block
languagejs
titleCeptor Gateway auth location
linenumberstrue
collapsetrue
{
  "name": "auth",
  "description": "Authentication application",
  "conditions": [{
    "type": "path",
    "deny": false,
    "lowercase": false,
    "values": ["/auth/*"]
  }],
  "conditions.type": "and",
  "location.enabled": true,
  "session.needed": true,
  "session.afterconditionsmatch": false,
  "session.override": false,
  "content.preload": false,
  "action": "serve",
  "response.compress": true,
  "cookiesnapper": {},
  "plugin": {},
  "webserver": {
    "path": "${ceptor.home}/auth",
    "case.sensitive": false,
    "directory.listing.enabled": false,
    "paths.canonicalize": true,
    "follow.links": false,
    "cache.enabled": true,
    "cache.changelistener": true,
    "response.notfound": {
      "response.status": 404,
      "response.reason": "Not found",
      "response.contenttype": "text/html; charset=utf8",
      "response.content": "<!DOCTYPE html>\n<html>\n<head><title>Not found<\/title><\/head>\n<body><h1>Not found :(<\/h1><\/body>\n<\/html>"
    }
  },
  "urlrewrite": [{
    "name": "remote_auth",
    "pattern": "/auth/(.*)",
    "newurl": "/$1",
    "decode.before.match": true,
    "clear.query.params": false,
    "redirect": false,
    "last": true
  }]
},

CeptorAuthenticate API

The CeptorAuthenticate API is called by JavaScript in the authentication application running in the browser.

The API offers these features:

  • Authenticate users using Multi Factor Authentication (MFA)
  • Create new users using self-service
  • Password reset

OpenAPI Specification

Below is the OpenAPI specification for this API:

Open api
defaultModelRenderingmodel
displayOperationIdtrue
showDownloadButtontrue
---
openapi: "3.0.0"
info:
  version: "1.0.0"
  title: "CeptorAuthenticate"
paths:
  /info:
    get:
      summary: "Gets info about currently authenticated user/session"
      operationId: "info"
      responses:
        200:Ceptor.registerTOTPPage = function() {
    $(locationName).html(
        '<div id="login-page" class="row">'+
        '    <div class="card">'+
        '        <div class="card-action blue white-text">'+
        '            <h3 class="center">Register Authenticator App</h3>'+
        '           <div class="black-text center">'+
        '              <h5>Please re-enter your password</h5>'+
        '            </div>'+
        '        </div>'+
        '    </div><br>'+
        '    <div class="card-content">'+
        '        <div class="input-field">'+
        '            <i class="material-icons prefix">lock_outline</i>'+
        '            <input id="password" type="password">'+
        '            <label for="password">Password</label>'+
        '        </div><br>'+
        '        <div class="form-field">'+
        '            <button id="loginbutton" class="btn-large waves-effect waves-light blue" style="width:100%" onclick="document.Ceptor.registerTOTPClicked()">Submit</button>'+
        '        </div>'+
        '    </div>'+
        '</div>'
    );

    $('#password').keyup(function() {
        if (event.keyCode === 13) {
            $('#loginbutton').click();
        }
    });

    $('#password').focus();
}

Ceptor.registerTOTPClicked = function() {
    var password = $('#password').val();

    if (!password) {
        $("#password").focus();
        M.toast({html: "Please enter a password"});
        return;
    }

    $(locationName).html(
        '<div id="totp-generation" class="row">'+
        '    <div class="card">'+
        '        <div class="card-action blue white-text">'+
        '            <h3 class="center">Register Authenticator App</h3>'+
        '            <div class="black-text center">'+
        '                <h5>Please hold, generating secret</h5>'+
        '            </div>'+
        '        </div>'+
        '    </div><br>'+
        '</div>');

    axios.post(pathPrefix+'/totp/qr', {
        password: password
    })
    .then(function (response) {
        // handle success
        $(locationName).html(
            '<div id="totp-register-page" class="row">'+
            '    <div class="card">'+
            '        <div class="card-action blue white-text">'+
            '            <h3 class="center">Register Authenticator App</h3>'+
            '           <div class="black-text center">'+
            '              <h5>Please scan QR code or enter secret in authenticator app</h5>'+
            '            </div>'+
            '        </div>'+
            '    </div><br>'+
            '    <div class="card-content">'+
            '        <div class="form-field">'+
            '          Secret Key: ' + response.data.secret + '<br/>'+
            '          <img src="data:image/png;base64,' + response.data.qr + '">'+
            '        </div>'+
            '        <div class="form-field">'+
            '            <button id="loginbutton" class="btn-large waves-effect waves-light blue" style="width:100%" onclick="document.Ceptor.loginPage()">Back</button>'+
            '        </div>'+
            '    </div>'+
            '</div>'
        );
    })
    .catch(function (error) {
        console.log(error);
        Ceptor.handleError("Unable to register TOTP", error);
        Ceptor.registerTOTPPage();
    });
}


Ceptor Gateway Location

To serve the application to the clients, on /auth/* you need to create a new Location in the gateway, from which the static files in the applications are served to the user.

Create a location called "auth", and section the action to "serve".

Image Added

Set conditions to match /auth/*

Image Added

and create an URL rewrite rule, which rewrites /auth/(.*) to /$1 - effectively stripping the /auth/ prefix from the request.

Image Added

Select the WebServer section, and point the webserver at ${ceptor.home}/auth where the html / javascript application is then served from.

Image Added


Below is a location configuration which can be copied into the gateway configuration, with the configuration described above.

Code Block
languagejs
titleCeptor Gateway auth location
linenumberstrue
collapsetrue
{
  "name": "auth",
  "description": "Authentication application",
  "conditions": [{
    "type": "path",
    "deny": false,
    "lowercase": false,
    "values": ["/auth/*"]
  }],
  "conditions.type": "and",
  "location.enabled": true,
  "session.needed": true,
  "session.afterconditionsmatch": false,
  "session.override": false,
  "content.preload": false,
  "action": "serve",
  "response.compress": true,
  "cookiesnapper": {},
  "plugin": {},
  "webserver": {
    "path": "${ceptor.home}/auth",
    "case.sensitive": false,
    "directory.listing.enabled": false,
    "paths.canonicalize": true,
    "follow.links": false,
    "cache.enabled": true,
    "cache.changelistener": true,
    "response.notfound": {
      "response.status": 404,
      "response.reason": "Not found",
      "response.contenttype": "text/html; charset=utf8",
      "response.content": "<!DOCTYPE html>\n<html>\n<head><title>Not found<\/title><\/head>\n<body><h1>Not found :(<\/h1><\/body>\n<\/html>"
    }
  },
  "urlrewrite": [{
    "name": "remote_auth",
    "pattern": "/auth/(.*)",
    "newurl": "/$1",
    "decode.before.match": true,
    "clear.query.params": false,
    "redirect": false,
    "last": true
  }]
},


CeptorAuthenticate API

The CeptorAuthenticate API is called by JavaScript in the authentication application running in the browser.

The API offers these features:

  • Authenticate users using Multi Factor Authentication (MFA)
  • Create new users using self-service
  • Password reset

OpenAPI Specification

Below is the OpenAPI specification for this API:

Open api
defaultModelRenderingmodel
displayOperationIdtrue
showDownloadButtontrue
---
openapi: "3.0.0"
info:
  version: "1.0.0"
  title: "Ceptauthenticate"
paths:
  /info:
    get:
      summary: "Gets info about currently authenticated user/session"
      operationId: "info"
      responses:
        "200":
          description: "Session Info"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Session"
        default:
          description: "unexpected error"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /auth/uidpw:
    post:
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AuthenticateRequest"
      summary: "Authenticate using userid/password"
      responses:
        "201":
          description: "Authentication successful"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Session"
        default:
          description: "unexpected error"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      description: "Authenticate a user using userid and password"
      operationId: "authuidpw"
      deprecated: false
      tags: []
  /auth/logoff:
    put:
      summary: "Logoff"
      description: "Logoff the user"
      operationId: "logoff"
      responses:
        "201":
          description: "Logoff completed"
        default:
          description: "unexpected error"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /auth/sendsms:
    put:
      summary: "Send SMS"
      description: "Send SMS to the user"
      operationId: "sendsms"
      responses:
        "201":
          description: "SMS sent"
        default:
          description: "unexpected error"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /auth/sendemail:
    put:
      summary: "Send Email"
      description: "Send OTP via email to the user"
      operationId: "sendemail"
      responses:
        "201":
          description: "Email sent"
        default:
          description: "unexpected error"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /auth/verifysmsotp:
    post:
      summary: "Verify SMS OTP"
      description: "Verifies OTP code earlier sent as SMS"
      operationId: "verifysms"
      responses:
        "201":
          description: "OK"
        default:
          description: "unexpected error"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      parameters:
      - name: "code"
        description: "Entered OTP code"
        in: "query"
        required: true
        allowEmptyValue: false
        deprecated: false
        schema:
          type: "string"
  /auth/verifytotp:
    post:
      summary: "Verify TOTP"
      description: "Verifies TOTP code (Google/MS etc. Authenticator App)"
      operationId: "verifytotp"
      responses:
        "201":
          description: "OK"
        default:
          description: "unexpected error"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      parameters:
      - name: "code"
        description: "Entered OTP code"
        in: "query"
        required: true
        allowEmptyValue: false
        deprecated: false
        schema:
          type: "string"
  /auth/verifyemailotp:
    post:
      summary: "Verify email OTP"
      description: "Verifies OTP code sent via email"
      operationId: "verifyemailotp"
      responses:
        "201":
          description: "OK"
        default:
          description: "unexpected error"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      parameters:
      - name: "code"
        description: "Entered OTP code"
        in: "query"
        required: true
        allowEmptyValue: false
        deprecated: false
        schema:
          type: "string"
  /initiateresetpassword:
    post:
      summary: "Initiate Reset password"
      description: "Initiate Reset password, prompting for OTP code - possibly sending\
        \ challenge using e.g. SMS or email"
      operationId: "init resetpassword"
      responses:
        "201":
          description: "OK"
        default:
          description: "unexpected error"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InitiateResetPasswordRequest"
  /resetpasswordotp:
    post:
      summary: "Reset password - verify OTP"
      description: "Verify OTP code"
      responses:
        "201":
          description: "OK"
        default:
          description: "unexpected error"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      requestBody:
        content:
          application/json:
            schema:
           description   $ref: "Session Info""#/components/schemas/ResetPasswordOTPRequest"
  /resetpassword:
    post:
      content:summary: "Reset password"
      description: "Resets your password, by answering  application/json:OTP challenge"
      operationId: "resetpassword"
      schemaresponses:
        "201":
       $ref   description: "#/components/schemas/SessionOK"
        default:
          description: "unexpected error"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /auth/uidpw:
    post:       requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AuthenticateRequest"/ResetPasswordRequest"
  /register:
    post:
      summary: "Register user"
      description: "Self registration"
      operationId: "register"
      responses:
       summary "201":
 "Authenticate using userid/password"       responsesdescription: "OK"
       201 default:
          description: "Authenticationunexpected successfulerror"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SessionError"
        defaultrequestBody:
          description: "unexpected error"
          content:
 
          application/json:
 
            schema:

               $ref: "#/components/schemas/ErrorRegisterRequest"
  /totp/qr:
    descriptionpost:
"Authenticate a user using userid and password      summary: "Generate TOTP secret and QR code"
      operationId: "authuidpwtotpqr"
      deprecatedresponses:
false        tags"200":
 []    /auth/logoff:     putdescription: "TOTP QR code"
    summary:  "Logoff"    content:
  description: "Logoff the user"       operationIdapplication/json: "logoff"
           responses:   schema:
     201:           description$ref: "Logoff completed#/components/schemas/TOTPQRResponse"
        default:
          description: "unexpected error"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

 /auth/sendsms:     putrequestBody:
      summary: "Send SMS"
      description content:
"Send SMS to the user"       operationId: "sendsms"application/json:
      responses:         201:
          description: "SMS sent"
  schema:
     default:           description$ref: "unexpected error#/components/schemas/TOTPQRRequest"
components:
  schemas:
    AuthenticateRequest:
  content:    required:
      -  application/json:"userid"
      properties:
        schemauserid:
          type: "string"
    $ref: "#/components/schemas/Error"   /auth/sendemailcredentials:

   put:       summarytype: "Send Emailstring"
    Session:
 description: "Send OTP via email toproperties:
the user"       operationIdis_delayed_logoff:
"sendemail"          responsestype: "boolean"
       201:   description: "True, if delayed logoff is initiated for description:this session"Email
sent"         defaultuser_id:
          descriptiontype: "unexpected errorstring"
          contentdescription: "User ID"
          application/jsoncustomer_id:
          type: "string"
  schema:        description: "Customer   ID"
    $ref: "#/components/schemas/Error"   /auth/verifysmsotp:
agreement_id:
   post:       summarytype: "string"Verify
  SMS OTP"       description: "VerifiesAgreement OTPID"
code earlier sent as SMS"    logged_on:
  operationId: "verifysms"       responsestype: "boolean"
       201:   description: "Indicates if a user is authenticated in description:this "OKsession"
        defaultinternal:
          descriptiontype: "unexpected errorboolean"
          contentdescription: "Is this user an internal user or not"
    application/json:    user_name:
          schematype: "string"
          description: "Users real name"
   $ref: "#/components/schemas/Error"    ip:
  parameters:       - nametype: "codestring"
          description: "Entered OTP codeIP address that created the session"
        ingroups:
 "query"         requiredtype: true"array"
        allowEmptyValue: false description: "Array of users groups"
   deprecated: false       items:
 schema:           type: "string"
  /auth/verifytotp:     post:       summarydescription: "VerifyGroup/Role TOTPname"
      description: "Verifies TOTP code (Google/MS etc. Authenticator App)" authentication_method:
           operationIdtype: "verifytotpinteger"
      responses:         201description: "Authentication          description: "OKmethod identifier"
        defaultauthentication_level:
          descriptiontype: "unexpected errorinteger"
          contentdescription: "Authentication level - the highger, the more secure"
    application/json:    creating_agent:
          schematype: "string"
               $refdescription: "#/components/schemas/Error"
      parameters:"Name of agent that created this session"
       - nameticket:
"code"          descriptiontype: "string"Entered
OTP code"         indescription: "query"Optional ticket for this session - can be JWT token, required:SAML\
true         allowEmptyValue: false  \ token, SSL clientcert or anything else deprecated:used falseas an alternate key to this\
   schema:         \ session type: "string"
  /auth/verifyemailotp:instead of the session ID"
    postResetPasswordRequest:
      summarydescription: "Verify emailReset password input, OTP" must be verified first, or this description:call\
"Verifies OTP code sent via email"   \ will fail"
 operationId: "verifyemailotp"    required:
  responses:    - "userid"
   201:   - "newpassword"
      descriptionproperties:
"OK"         defaultuserid:
          description: "unexpected errorUserid"
          contenttype: "string"
        newpassword:
    application/json:      description: "New password"
      schema:    type: "string"
    ResetPasswordOTPRequest:
      $refdescription: "#/components/schemas/ErrorReset password OTP input"
      parametersrequired:
      - name: "codeuserid"
        description:- "Entered OTP codeemail"
        in:- "query"
  otptype"
     required: true- "code"
       allowEmptyValueproperties:
false        userid:
deprecated: false         schemadescription: "Userid"
          type: "string"
   /initiateresetpassword:     postemail:
      summary: "Initiate Reset password" description: "Email address - must match description:existing record"Initiate
 Reset password, prompting for OTP code - possibly sending\ type: "string"
      \ challenge using e.g. SMS or email"otptype:
          operationIddescription: "initType of resetpasswordotp"
        responses:  enum:
          - 201:"totp"
          description:- "OKemail"
          - default:"sms"
          descriptiontype: "unexpected errorstring"
        code:
 content:         description: "OTP code"
 application/json:         type: "string"
    schemaInitiateResetPasswordRequest:
      description: "Initiate Reset password input - used when starting a $ref: "#/components/schemas/Error"request to\
      requestBody:  \ ask for password reset, e.g. to content:OTP code to be submitted"
      application/jsonrequired:
      - "userid"
    schema:  - "email"
      -  "otptype"
  $ref: "#/components/schemas/InitiateResetPasswordRequest"   /resetpasswordotpproperties:

   post:       summaryuserid:
"Reset password - verify OTP"
      description: "Userid"Verify
  OTP code"       responsestype: "string"
        201email:
          description: "OK"Email address - must match existing  record"
 default:           descriptiontype: "string"unexpected
error"        otptype:
  content:        description: "Type of otp"
 application/json:         enum:
     schema:     - "totp"
          $ref:- "#/components/schemas/Erroremail"
      requestBody:    - "sms"
    content:      type: "string"
    application/jsonRegisterRequest:
      description: "Registration form"
      schemarequired:
      - "firstname"
      $ref:- "#/components/schemas/ResetPasswordOTPRequestlastname"
  /resetpassword:    - post:"userid"
      summary:- "Reset password"
      description:- "email"Resets
  your password, by answering OTP- challenge"mobile"
      operationIdproperties:
"resetpassword"        responsesfirstname:
        201:  description: "First name"
          descriptiontype: "OKstring"
        defaultlastname:
          description: "unexpectedLast errorname"
          content:type: "string"
        userid:
     application/json:     description: "Userid"
          type: "string"
  schema:      password:
          $refdescription: "#/components/schemas/Error"Password"
          requestBodytype: "string"
        contentemail:
          application/jsondescription: "Email address"
          schematype: "string"
        mobile:
    $ref: "#/components/schemas/ResetPasswordRequest"      /registerdescription: "Mobile number"
  post:        summarytype: "Register user"string"
    TOTPQRRequest:
    description: "Self registration"required:
      operationId:- "registerpassword"
      responsesproperties:
        201password:
          descriptiontype: "OKstring"
        default  format: "password"
          description: "unexpectedUser errorpassword"
    TOTPQRResponse:
     content required:
      - "qr"
    application/json:  - "secret"
      properties:
    schema:    qr:
            $reftype: "#/components/schemas/Error"string"
        requestBody:  format: "base64"
        content  description: "QR Code in PNG format"
      application/json:  secret:
          schematype: "string"
          description: "Secret value $ref:to "#/components/schemas/RegisterRequest"
components:register"
    schemasError:
    AuthenticateRequest  required:
      required:- "code"
      - "useridmessage"
      properties:
        useridcode:
          type: "stringinteger"
         credentials format: "int32"
         type: "string"message:
    Session:       propertiestype:         is_delayed_logoff:
          type: "boolean"
"string"
security: []
servers:
- url: "https://localhost:8443/ceptorauthenticate/1"
  description: "Sandbox environment, used for initial testing of APIs, playing around\
    \ with new   description: "True, if delayed logoff is initiated for this session"
        user_id:
          type: "string"
          description: "User ID"
        customer_id:
          type: "string"
          description: "Customer ID"
        agreement_id:
          type: "string"
          description: "Agreement ID"
        logged_on:
          type: "boolean"
          description: "Indicates if a user is authenticated in this session"
        internal:
          type: "boolean"
          description: "Is this user an internal user or not"
        user_name:
 versions"

Implementation

This API is implemented as scripts within the gateway, below is example of the implementation for each operation in the API:

Note that this implementation uses some of the Authentication Plugins included with Ceptor - these plugins use Ceptors own Ceptor User Administration Server and UserAdmin API to store users. You should modify the implementation to suit your requirements and plugins, in case they differ.

Warning

You should remove any operations that offers functionality which you do not wish to expose to your clients - if you e.g. do not want to offer self-registration to clients, remove the part for the UI, and remove the relevant API offering this functionality.

GET /info

This operation returns information about the current session, for use by the application.

Code Block
languagejs
titleImplementation for GET /info
linenumberstrue
collapsetrue
var pSessionId = context.id;

var isAuthenticated = context.agent.isLoggedOn( pSessionId );

var response = {
    // Is delayed logoff or not
    "is_delayed_logoff": context.agent.isDelayedLogoff(pSessionId),
	// User ID
	"user_id": context.agent.getUser(pSessionId),
	// Customer ID
	"customer_id": isAuthenticated ? context.agent.getCustomerID(pSessionId) : null,
	// Agreement ID
	"agreement_id": isAuthenticated ? context.agent.getAgreementID(pSessionId) : null,
	// Is internal user
	"internal": isAuthenticated ? context.agent.isInternalUser(pSessionId) : null,
	// Is logged on
	"logged_on": isAuthenticated,
	// Users real name
	"user_name": context.agent.getUserName(pSessionId),
	// IP address that created the session
	"ip": context.agent.getUserIP(pSessionId),
	// Authentication level
	"authentication_level": context.agent.getAuthenticationLevel(pSessionId),
	// Authentication method
	"authentication_method": context.agent.getAuthenticationMethod(pSessionId),
	// Array of users groups
	"groups": null,
	// Name of creating agent
	"creating_agent": context.agent.getNameOfCreatingAgent(pSessionId),
	// Ticket or token
	"ticket": context.agent.getTicketFromSession(pSessionId)
};

try {
    response.groups = Java.from(context.agent.getUserGroups(pSessionId));
} catch(e) {}

context.respond(200, 'OK', 'application/json', JSON.stringify(response));

POST /auth/uidpw

This operation supports authenticating using userid and password. By default, it treats this as first step of a multifactor authentication, where the user must complete authentication using either OTP sent as SMS, OTP sent as Email, or a TOTP code using a previously registered Authenticator application.

If you remove this line:

Code Block
 context.agent.setStateVariable(pSessionId, "require_otp", "true", false);

then two-factor authentication will be disabled, and login will complete after the first userid/password login.

Code Block
languagejs
titleImplementation of POST /auth/uidpw
linenumberstrue
collapsetrue
function auth() {
    var req = JSON.parse(context.getRequestBodyAsString());
    
   type: "string"
          description context.trace.trace("Authenticate called with userid: "Users real name"+ req.userid);

    try   ip:{
          type: "string"
          description: "IP address that created the session"
 var pSessionId = context.id;
      groups:  
        type:context.agent.setStateVariable(pSessionId, "arrayrequire_otp", "true", false);
        description: "Array of users groups"context.agent.logon(pSessionId, req.userid, req.credentials);
        
 items:       var isAuthenticated = context.agent.isLoggedOn(pSessionId);
  type: "string"     
       description: "Group/Role name"
  var response = {
      authentication_method:      // Is delayed logoff or type:not
"integer"           description: "Authentication method identifier"
is_delayed_logoff": context.agent.isDelayedLogoff(pSessionId),
       authentication_level:
   	// User ID
       type: 	"integeruser_id": context.agent.getUser(pSessionId),
        	// description: "Authentication level - the highger, the more secure"
        creating_agent:Customer ID
        	"customer_id": isAuthenticated ? context.agent.getCustomerID(pSessionId) : null,
        	// Agreement ID
type: "string"       	"agreement_id": isAuthenticated ?  description: "Name of agent that created this session"context.agent.getAgreementID(pSessionId) : null,
        	// Is internal user
 ticket:       	"internal": isAuthenticated ?  type: "string"context.agent.isInternalUser(pSessionId) : null,
        	// Is logged description:on
"Optional ticket for this session - can be JWT token, SAML\ 	"logged_on": isAuthenticated,
        	// Users real name
 \ token, SSL clientcert or anything else used as an alternate key to this\ 	"user_name": context.agent.getUserName(pSessionId),
        	// IP address that created the session
  \ session instead of the session ID	"ip": context.agent.getUserIP(pSessionId),
    ResetPasswordRequest:    	// Authentication level
description:  "Reset password input, OTP must be verified first, or this call\ 	"authentication_level": context.agent.getAuthenticationLevel(pSessionId),
        	// Authentication method
\ will fail"       required:
	"authentication_method": context.agent.getAuthenticationMethod(pSessionId),
     - "userid"  	// Array of users groups
- "newpassword"       properties:	"groups": null,
        userid:
   	// Name of creating agent
       description: 	"Useridcreating_agent": context.agent.getNameOfCreatingAgent(pSessionId),
         type: "string"	// Ticket or token
        newpassword	"ticket": context.agent.getTicketFromSession(pSessionId),
        	// description:Available "NewOTP password"methods
          type: "string"
	"otpmethods": []
   ResetPasswordOTPRequest:     };
 description: "Reset password OTP input"   
   required:     try {
- "userid"       - "email"   response.groups = Java.from(context.agent.getUserGroups(pSessionId));
 - "otptype"      } - "code"catch(e) {}
      properties:  
      userid:  var otpMethods = context.agent.getStateVariable(pSessionId, "otpmethods");
    description: "Userid"   if (otpMethods) {
     type: "string"      response.otpmethods =  email:otpMethods.split(";");
        }

 description: "Email address - must match existing record"
   context.respond(200, 'OK', 'application/json', JSON.stringify(response));
       type: "string"
} catch(err) {
       otptype: if (! (err instanceof Java.type('java.lang.Throwable')))
     description: "Type of otp"    throw err;
     enum:       
   - "totp"    context.trace.trace("Authentication failed", err);
    - "email"   if (err instanceof Java.type('dk.itp.security.passticket.PTException')) {
   - "sms"        var response = type:{
"string"         code:       "code": err.getErrorCode(),
  description: "OTP code"           type: "string"message": err.getErrorText()
    InitiateResetPasswordRequest:       description: "Initiate};
Reset password input - used when starting a request to\   
     \ ask for password reset, e.g. to OTPif (response.code == to15) be{ submitted"// Need OTP
    required:       - "userid"    var otpmethods  -= context.agent.getStateVariable(pSessionId, "emailotpmethods");
      - "otptype"       properties:  if (otpmethods)
     userid:           description: "Userid"   response.otpmethods        type: "string"= otpmethods.split(';');
          email:  }
        description: "Email address - must match existing record"if (!response.message)
            type: "string"   response.message = "Unknown error";
  otptype:           description: "Type of otp"
          enum:  context.respond(401, 'Authentication Failed', 'application/json', JSON.stringify(response));
    - "totp"   } else {
     - "email"      throw err;
   - "sms"    }
    }
}
type: "string"
    RegisterRequest:
      description: "Registration form"
auth();

POST /auth/logoff

This operation simply logs off the user.

Code Block
languagejs
titleImplementation of PUT /auth/logoff
linenumberstrue
collapsetrue
context.agent.logoff(context.id);
context.respond(201, 'OK', 'application/json', '');

POST /auth/sendsms

This operation generates an OTP code and sends it using SMS/Text to the users mobile number.

Code Block
languagejs
titleImplementation of POST /auth/sendsms
linenumberstrue
collapsetrue
try {
    context.agent.newToken(context.id,43,"sendsms");
    
 required:   context.respond(201, 'SMS Sent',  - "firstname"
'application/json','');
} catch(err) {
    if - "lastname"
 (! (err instanceof Java.type('java.lang.Throwable')))
    - "userid"   throw err;
  - "password"     
 - "email"       - "mobile"
context.trace.trace("SendSMS failed", err);
    if properties:(err instanceof Java.type('dk.itp.security.passticket.PTException')) {
     firstname:   var response       description: "First name"= {
           type: "stringcode"
        lastname:: err.getErrorCode(),
            description: "Last name"
"message": err.getErrorText()
        };
type: "string"       
 userid:       if (!response.message)
  description: "Userid"         response.message = type:"Unknown error"string";
        password:     
     description: "Password"  context.respond(400, 'Send SMS Failed', 'application/json', JSON.stringify(response));    type:
"string"    } else {
  email:      throw err;
   description: "Email address"
  }
}

POST /auth/sendemail

This operation generates an OTP and sends it using email to the users email address.

Code Block
languagejs
titleImplementation of PUT /auth/sendemail
linenumberstrue
collapsetrue
try {
    context.agent.newToken(context.id,50,"emailotp");
    
  type: "string"
        mobile:
          description: "Mobile number"
    context.respond(201, 'Email Sent', 'application/json','');
} catch(err) {
    if (! (err instanceof Java.type('java.lang.Throwable')))
        throw err;
      type: "string" 
   Error:
    context.trace.trace("Send Email failed", err);
  required:  if (err instanceof Java.type('dk.itp.security.passticket.PTException')) {
- "code"       -var "message"response = {
    properties:         "code": err.getErrorCode(),
         type: "integer"           format: "int32"
"message": err.getErrorText()
       message: };
        
type: "string"

Implementation

This API is implemented as scripts within the gateway, below is example of the implementation for each operation in the API:

Note that this implementation uses some of the Authentication Plugins included with Ceptor - these plugins use Ceptors own Ceptor User Administration Server and UserAdmin API to store users. You should modify the implementation to suit your requirements and plugins, in case they differ.

Warning

You should remove any operations that offers functionality which you do not wish to expose to your clients - if you e.g. do not want to offer self-registration to clients, remove the part for the UI, and remove the relevant API offering this functionality.

GET /info

This operation returns information about the current session, for use by the application.

Code Block
languagejs
titleImplementation for GET /info
linenumberstrue
collapsetrue
var pSessionId = context.id;

var isAuthenticated = context.agent.isLoggedOn( pSessionId );

var response = {
    // Is delayed logoff or not
    "is_delayed_logoff": context.agent.isDelayedLogoff(pSessionId),
	// User ID
	"user_id": context.agent.getUser(pSessionId),
	// Customer ID
	"customer_id": isAuthenticated ? context.agent.getCustomerID(pSessionId) : null,
	// Agreement ID
	"agreement_id": isAuthenticated ? context.agent.getAgreementID(pSessionId) : null,
	// Is internal user
	"internal": isAuthenticated ? context.agent.isInternalUser(pSessionId) : null,
	// Is logged on
	"logged_on": isAuthenticated,
	// Users real name
	"user_name": context.agent.getUserName(pSessionId),
	// IP address that created the session
	"ip": context.agent.getUserIP(pSessionId),
	// Authentication level
	"authentication_level": context.agent.getAuthenticationLevel(pSessionId),
	// Authentication method
	"authentication_method": context.agent.getAuthenticationMethod(pSessionId),
	// Array of users groups
	"groups": null,
	// Name of creating agent
	"creating_agent": context.agent.getNameOfCreatingAgent(pSessionId),
	// Ticket or token
	"ticket": context.agent.getTicketFromSession(pSessionId)
};

try {
    response.groups = Java.from(context.agent.getUserGroups(pSessionId));
} catch(e) {}

context.respond(200, 'OK', 'application/json', JSON.stringify(response));

POST /auth/uidpw

This operation supports authenticating using userid and password. By default, it treats this as first step of a multifactor authentication, where the user must complete authentication using either OTP sent as SMS, OTP sent as Email, or a TOTP code using a previously registered Authenticator application.

If you remove this line:

Code Block
 context.agent.setStateVariable(pSessionId, "require_otp", "true", false);

then two-factor authentication will be disabled, and login will complete after the first userid/password login.

Code Block
languagejs
titleImplementation of POST /auth/uidpw
linenumberstrue
collapsetrue
function auth() {
    var req = JSON.parse(context.getRequestBodyAsString())       if (!response.message)
            response.message = "Unknown error";
            
        context.respond(400, 'Send Email Failed', 'application/json', JSON.stringify(response));    
    } else {
        throw err;
    }
}

POST /auth/verifysmsotp

This operation is used for verifying an entered OTP value sent earlier using SMS/Text message.

Code Block
languagejs
titleImplementation of POST /auth/verifysmsotp
linenumberstrue
collapsetrue
try {
    var credentials = [pCode];
    
    context.agent.logon(context.id,43,context.agent.getUser(context.id), Java.to(credentials, "java.lang.String[]"));
    
    context.respond(201, 'OK', 'application/json', '');
} catch(err) {
    if (! (err instanceof Java.type('java.lang.Throwable')))
        throw err;
        
    context.trace.trace("VerifySMS failed", err);
    if (err instanceof Java.type('dk.itp.security.passticket.PTException')) {
        var response = {
            "code": err.getErrorCode(),
            "message": err.getErrorText()
        };
        
context.trace.trace("Authenticate called with userid: " + req.userid);        if (!response.message)
      try {     response.message = "Unknown error";
var pSessionId = context.id;         
        context.agent.setStateVariablerespond(pSessionId, "require_otp", "true", false400, 'Verify SMS OTP Failed', 'application/json', JSON.stringify(response));    
    context.agent.logon(pSessionId, req.userid, req.credentials);
} else {
        throw err;
    }
}

POST /auth/verifytotp

This operation is used for verifying a TOTP code entered using a previously registered TOTP authenticator.

Code Block
languagejs
titleImplementation of POST /auth/verifytotp
linenumberstrue
collapsetrue
try {
    var credentials = [pCode];
    
   var isAuthenticated =  context.agent.logon(context.id,45,context.agent.isLoggedOngetUser(pSessionIdcontext.id);
 , Java.to(credentials, "java.lang.String[]"));
    
    context.respond(201, 'OK', 'application/json', '');
} catch(err) var{
response = {  if (! (err instanceof Java.type('java.lang.Throwable')))
      // Is delayed logoffthrow orerr;
not        
    "is_delayed_logoff": context.agenttrace.isDelayedLogoff(pSessionId),
  trace("Verify TOTP failed", err);
    if 	// User ID
        	"user_id": context.agent.getUser(pSessionId),
        	// Customer ID
        	"customer_id": isAuthenticated ? context.agent.getCustomerID(pSessionId) : null,
        	// Agreement ID(err instanceof Java.type('dk.itp.security.passticket.PTException')) {
        var response = {
            	"agreement_idcode": isAuthenticated ? context.agent.getAgreementID(pSessionId) : null,
   err.getErrorCode(),
            "message": err.getErrorText()
    	// Is internal user };
       	"internal": isAuthenticated
? context.agent.isInternalUser(pSessionId) : null,     if (!response.message)
  	// Is logged on       response.message = 	"logged_on": isAuthenticated,"Unknown error";
         	// Users real name 
       	"user_name": context.agent.getUserName(pSessionId),respond(400, 'Verify TOTP Failed', 'application/json', JSON.stringify(response));    
    	//} IPelse address{
that created the session     throw err;
  	"ip": context.agent.getUserIP(pSessionId),
   }
}

POST /auth/verifyemailotp

This operation is used for verifying an OTP code earlier sent as email.

Code Block
languagejs
titleImplementation of POST /auth/verifyemailotp
linenumberstrue
collapsetrue
try {
    var 	//credentials Authentication level= [pCode];
    
   	"authentication_level": context.agent.getAuthenticationLevel(pSessionId.logon(context.id,50,context.agent.getUser(context.id),     Java.to(credentials, "java.lang.String[]"));
   	// Authentication
method    context.respond(201, 'OK', 'application/json', '');
 	"authentication_method": context.agent.getAuthenticationMethod(pSessionId),
} catch(err) {
    if (! (err 	// Array of users groupsinstanceof Java.type('java.lang.Throwable')))
        throw err;
 	"groups": null,      
  	// Name of creating agent
  context.trace.trace("Verify email OTP failed", err);
    if  	"creating_agent": context.agent.getNameOfCreatingAgent(pSessionId),(err instanceof Java.type('dk.itp.security.passticket.PTException')) {
         	// Ticket or tokenvar response = {
            	"ticketcode": contexterr.agent.getTicketFromSessiongetErrorCode(pSessionId),
     
  	// Available OTP methods         	"otpmethodsmessage": []err.getErrorText()
        };
        
        try {if (!response.message)
            response.groupsmessage = Java.from(context.agent.getUserGroups(pSessionId))"Unknown error";
        } catch(e) {}  
        context.respond(400, 'Verify email OTP Failed',   var otpMethods = context.agent.getStateVariable(pSessionId, "otpmethods"'application/json', JSON.stringify(response));    
    if} (otpMethods)else {
        throw err;
     response.otpmethods = otpMethods.split(";"}
}

POST /initiateresetpassword

This operation initiates a reset password flow - this uses an entered userid/email address, as well as an otptype specyfing if sms, totp or email shoudl be used.
It then compares them against known information, and sends out the OTP code if relevant using the chosen mechanism.

Code Block
languagejs
titleImplementation of POST /initiateresetpassword
linenumberstrue
collapsetrue
var body = context.getRequestBodyAsString();

try {
     }context.agent.executeAuthpluginCommand(context.id, 9 , 'initresetpassword', body);
    
    context.respond(200201, 'Password 'OKReset', 'application/json', JSON.stringify(response)'');
    } catch(err) {
        if (! (err instanceof Java.type('java.lang.Throwable')))
   
        throw err;
 
        
          context.trace.trace("AuthenticationPassword reset failed", err);
   
    if (err instanceof Java.type('dk.itp.security.passticket.PTException')) {
   
        var response = {
  
             "code": err.getErrorCode(),
   
            "message": err.getErrorText()
 
          };
            
            if (response.code == 15) { // Need OTP
                var otpmethods = context.agent.getStateVariable(pSessionId, "otpmethods");
                if (otpmethods)
                    response.otpmethods = otpmethods.split(';');
        
   }             if (!response.message)
   
            response.message = "Unknown error";
           
        
        context.respond(401400, 'AuthenticationPassword reset Failedfailed', 'application/json', JSON.stringify(response));    
    } else {
   
        throw err;

       }

   }
}

auth();

POST /

...

resetpasswordotp

This operation simply logs off the useris called after /initiateresetpassword with the entered OTP, to validate if it is correct.

Code Block
languagejs
titleImplementation of PUT POST /auth/logoffresetpasswordotp
linenumberstrue
collapsetrue
var body = context.agent.logoff(context.idgetRequestBodyAsString();
context.respond(201,
'OK', 'application/json', '');

POST /auth/sendsms

This operation generates an OTP code and sends it using SMS/Text to the users mobile number.

Code Block
languagejs
linenumberstrue
collapsetrue
try {
    context.agent.newTokenexecuteAuthpluginCommand(context.id,43,"sendsms" 9 , 'resetpasswordotp', body);
    
    context.respond(201, 'SMSPassword SentReset', 'application/json', '');
} catch(err) {
    if (! (err instanceof Java.type('java.lang.Throwable')))
        throw err;
        
    context.trace.trace("SendSMSPassword reset failed", err);
    if (err instanceof Java.type('dk.itp.security.passticket.PTException')) {
        var response = {
            "code": err.getErrorCode(),
            "message": err.getErrorText()
        };
        
        if (!response.message)
            response.message = "Unknown error";
   
        
        context.respond(400, 'SendPassword SMSreset Failedfailed', 'application/json', JSON.stringify(response));    
    } else {
        throw err;
    }
}

POST /

...

resetpassword

This operation generates an OTP and sends it using email to the users email addressis the last step of a password reset, after the OTP has been validated. It allows the user to choose a new password.

Code Block
languagejs
titleImplementation of PUT POST /auth/sendemailresetpassword
linenumberstrue
collapsetrue
var body = context.getRequestBodyAsString();

try {
    context.agent.newTokenexecuteAuthpluginCommand(context.id,50,"emailotp"id, 9 , 'resetpassword', body);
    
    context.respond(201, 'EmailPassword SentReset', 'application/json', '');
} catch(err) {
    if (! (err instanceof Java.type('java.lang.Throwable')))
        throw err;
        
    context.trace.trace("SendPassword Emailreset failed", err);
    if (err instanceof Java.type('dk.itp.security.passticket.PTException')) {
        var response = {
            "code": err.getErrorCode(),
            "message": err.getErrorText()
        };
        
        if (!response.message)
            response.message = "Unknown error";
            
        context.respond(400, 'Send Email Failed', 'application/json', JSON.stringify(response));    
    } else {
        throw err;
    }
}

POST /auth/verifysmsotp

This operation is used for verifying an entered OTP value sent earlier using SMS/Text message.

Code Block
languagejs
titleImplementation of POST /auth/verifysmsotp
linenumberstrue
collapsetrue
try {
    var credentials = [pCode];
    
    context.agent.logon(context.id,43,context.agent.getUser(context.id), Java.to(credentials, "java.lang.String[]")); error";
        
        context.respond(201400, 'OKPassword reset failed', 'application/json', ''JSON.stringify(response)); } catch(err) { 
   if (!} (err instanceof Java.type('java.lang.Throwable')))else {
        throw err;
    }
}



    context.trace.trace("VerifySMS failed", err);

POST /register

This operation is used for registering new users, allowing them to input their information and get an account created.

Code Block
languagejs
titleImplementation of POST /register
linenumberstrue
collapsetrue
function register() {
    var ifreq (err instanceof Java.type('dk.itp.security.passticket.PTException')) {= JSON.parse(context.getRequestBodyAsString());
    
    var response = {
  context.trace.trace("Register called with userid: " + req.userid);

    try {
  "code": err.getErrorCode(),     var pSessionId = context.id;
    "message": err.getErrorText()   var CreateUserCredentials     }= Java.type("dk.itp.security.passticket.CreateUserCredentials");
        var cc =       if (!response.message)new CreateUserCredentials();
            response.messagecc.userid = "Unknown error"req.userid;
        cc.password = req.password;
        cc.firstname = context.respond(400, 'Verify SMS OTP Failed', 'application/json', JSON.stringify(response));    
req.firstname;
        cc.lastname = req.lastname;
   } else {   cc.email = req.email;
   throw err;    cc.mobile }
}

POST /auth/verifytotp

This operation is used for verifying a TOTP code entered using a previously registered TOTP authenticator.

Code Block
languagejs
titleImplementation of POST /auth/verifytotp
linenumberstrue
collapsetrue
try {= req.mobile;
     var credentials = [pCode]; 
        context.agent.logon(context.id,45,context.agent.getUser(context.id), Java.to(credentials, "java.lang.String[]"));pSessionId, 9, req.userid, cc);
        
        context.respond(201, 'OKRegistration successful', 'application/json', '');
    } catch(err) {
        if (! (err instanceof Java.type('java.lang.Throwable'))))
            throw err;
            
        context.trace.trace("VerifyRegistration TOTP failed", err);
        if (err instanceof Java.type('dk.itp.security.passticket.PTException')) {
            var response = {
                "code": err.getErrorCode(),
                "message": err.getErrorText()
        };    };
            
if (!response.message)           if  (!response.message = "Unknown error";)
                      context.respond(400, 'Verify TOTP Failed', 'application/json', JSON.stringify(response));    
    } else {
        throw err;
    }
}

POST /auth/verifyemailotp

This operation is used for verifying an OTP code earlier sent as email.

Code Block
languagejs
titleImplementation of POST /auth/verifyemailotp
linenumberstrue
collapsetrue
try {response.message = "Unknown error";
     var credentials = [pCode];        
 context.agent.logon(context.id,50,context.agent.getUser(context.id), Java.to(credentials, "java.lang.String[]"));          context.respond(201401, 'OKRegistration Failed', 'application/json', ''JSON.stringify(response));
 } catch(err) {       } else if{
(! (err instanceof Java.type('java.lang.Throwable')))         throw err;
        }
    context.trace.trace("Verify email OTP failed", err);
}
}

register();

POST /totp/qr

This operation is used for registering a new Authenticator Application by retrieving the QR code and secret that can be registered in a mobile authenticator such as MS Authenticator or Google Authenticator

Code Block
languagejs
titleImplementation of POST /totp/qr
linenumberstrue
collapsetrue
try {
    if (err instanceof Java.type('dk.itp.security.passticket.PTException'!context.agent.isLoggedOn(context.id)) {
        var response = {
            "code": err.getErrorCode(), 18, // AuthErrorCodes.ERROR_NOTAUTHENTICATED
            "message": err.getErrorText()"Requires authentication"
        };
        
        context.respond(403, 'Denied not logged on', 'application/json', JSON.stringify(response));
    } else {
        var body if= JSON.parse(!response.message)context.getRequestBodyAsString());

        var   response.message = "Unknown{
error";        	// QR Code in PNG format
        context.respond(400, 'Verify email OTP Failed', 'application/json', JSON.stringify(response)); 	// Format: base64
       } else	// {Secret value to register
     throw err;     }
}

POST /initiateresetpassword

This operation initiates a reset password flow - this uses an entered userid/email address, as well as an otptype specyfing if sms, totp or email shoudl be used.
It then compares them against known information, and sends out the OTP code if relevant using the chosen mechanism.

Code Block
languagejs
titleImplementation of POST /initiateresetpassword
linenumberstrue
collapsetrue
var body = context.getRequestBodyAsString();

try {
    context.agent.executeAuthpluginCommand(context.id, 9 , 'initresetpassword', body);
	"secret": context.agent.newToken(context.id,45, "secretpassword;"+body.password),
        	"qr": context.agent.newToken(context.id,45, "PNG")
        };
        context.respond(201200, 'Password ResetOK', 'application/json', ''JSON.stringify(response));
    }
} catch(err) {
    if (! (err instanceof Java.type('java.lang.Throwable')))
        throw err;
        
    context.trace.trace("PasswordQR Code resetregistration failed", err);
    if (err instanceof Java.type('dk.itp.security.passticket.PTException')) {
        var response = {
            "code": err.getErrorCode(),
            "message": err.getErrorText()
        };
        
        if (!response.message)
            response.message = "Unknown error";
            
        context.respond(400, 'PasswordQR Code resetregistration failed', 'application/json', JSON.stringify(response));    
    } else {
        throw err;
    }
}

POST /resetpasswordotp

This operation is called after /initiateresetpassword with the entered OTP, to validate if it is correct.

Code Block
languagejs
titleImplementation of POST /resetpasswordotp
linenumberstrue
collapsetrue
var body = context.getRequestBodyAsString();

try {
    context.agent.executeAuthpluginCommand(context.id, 9 , 'resetpasswordotp', body);
    
    context.respond(201, 'Password Reset', 'application/json', '');
} catch(err) {    
if (! (err instanceof Java.type('java.lang.Throwable')))  } else {
        throw err;
    }
}


Import API directly

If you prefer, you can create a new API from this API Version specification directly.

Code Block
languagejs
titleAPI Version JSON Specification
linenumberstrue
collapsetrue
{
  "implementation": {"Sandbox": {
  context.trace.trace("Password reset failed"", err);
: {"*": {
   if (err instanceof Java.type('dk.itp.security.passticket.PTException')) { "proxy": {},
      "script":  "var response = {\n  text: 'Sorry, this API operation has not yet been implemented.'\n};\ncontext.respond(200,  "code": err.getErrorCode()'OK', 'application/json', JSON.stringify(response));",
      "pipeline": {},
      "messagetype": err.getErrorText()
   "script"
    }},
    "/hello": {"get": {
     }; "proxy": {},
      "script": "var response = {\n\t\"text\": \"a string\"\n};\ncontext.respond(200, 'OK',  if (!response.message)'application/json', JSON.stringify(response));\n",
      "pipeline": {},
    response.message = "Unknown error";type": "script"
    }},
    "/auth/uidpw": {"post":  {
    context.respond(400, 'Password reset failed', 'application/json', JSON.stringify(response)); "proxy": {},
         } else {"script": "%{script}function auth() {\n    var req    throw err;= JSON.parse(context.getRequestBodyAsString());\n     }
}

POST /resetpassword

This operation is the last step of a password reset, after the OTP has been validated. It allows the user to choose a new password.

Code Block
languagejs
titleImplementation of POST /resetpassword
linenumberstrue
collapsetrue
var body = context.getRequestBodyAsString();

try {
    context.agent.executeAuthpluginCommand(context.id, 9 , 'resetpassword', body);
 \n    context.trace.trace(\"Authenticate called with userid: \" + req.userid);\n\n    try {\n        var pSessionId = context.id;\n        \n        context.respondagent.setStateVariable(201pSessionId, 'Password Reset', 'application/json', '');
} catch(err) {\"require_otp\", \"true\", false);\n       if (! (err instanceof Java.type('java.lang.Throwable')))context.agent.logon(pSessionId, req.userid, req.credentials);\n        \n throw err;      var isAuthenticated  
   = context.traceagent.trace("Password reset failed", errisLoggedOn(pSessionId);\n     if (err instanceof Java.type('dk.itp.security.passticket.PTException')) { \n        var response = {\n             "code": err.getErrorCode(),
// Is delayed logoff or not\n            "message": err.getErrorText()\"is_delayed_logoff\": context.agent.isDelayedLogoff(pSessionId),\n         };
\t// User ID\n        \t\"user_id\": context.agent.getUser(pSessionId),\n        \t// if (!response.message)Customer ID\n        \t\"customer_id\": isAuthenticated ? context.agent.getCustomerID(pSessionId)  response.message = "Unknown error";: null,\n        \t// Agreement ID\n        \t\"agreement_id\": isAuthenticated ? context.respond(400, 'Password reset failed', 'application/json', JSON.stringify(response));  agent.getAgreementID(pSessionId) : null,\n       } else {
  \t// Is internal user\n      throw err;
    }
}


POST /register

This operation is used for registering new users, allowing them to input their information and get an account created.

Code Block
languagejs
titleImplementation of POST /register
linenumberstrue
collapsetrue
function register() {
    var req = JSON.parse(context.getRequestBodyAsString());
    
    context.trace.trace("Register called with userid: " + req.userid); \t\"internal\": isAuthenticated ? context.agent.isInternalUser(pSessionId) : null,\n        \t// Is logged on\n        \t\"logged_on\": isAuthenticated,\n        try {
 \t// Users real name\n       var pSessionId = context.id; \t\"user_name\": context.agent.getUserName(pSessionId),\n        \t// varIP CreateUserCredentialsaddress = Java.type("dk.itp.security.passticket.CreateUserCredentials");
 that created the session\n       var cc = new CreateUserCredentials(); \t\"ip\": context.agent.getUserIP(pSessionId),\n         cc.userid = req.userid;\t// Authentication level\n          cc.password = req.password;\t\"authentication_level\": context.agent.getAuthenticationLevel(pSessionId),\n         cc.firstname = req.firstname;\t// Authentication method\n          cc.lastname = req.lastname;\t\"authentication_method\": context.agent.getAuthenticationMethod(pSessionId),\n        \t// Array of cc.email = req.email;users groups\n        \t\"groups\":  cc.mobile = req.mobile;null,\n        \t// Name of creating agent\n        \t\"creating_agent\": context.agent.logongetNameOfCreatingAgent(pSessionId),\n 9, req.userid, cc);     \t// Ticket or token\n         \t\"ticket\": context.agent.respondgetTicketFromSession(201pSessionId),\n 'Registration successful', 'application/json', '');    \t// } catch(err) {Available OTP methods\n         if (! (err instanceof Java.type('java.lang.Throwable')))\t\"otpmethods\": []\n             throw err;};\n        \n              context.trace.trace("Registration failed", err);try {\n           if (err instanceofresponse.groups = Java.typefrom('dkcontext.itp.security.passticket.PTException')) {agent.getUserGroups(pSessionId));\n        } catch(e) {}\n    var  response = {\n        var otpMethods = context.agent.getStateVariable(pSessionId, \"otpmethods\");\n     "code": err.getErrorCode(),  if (otpMethods) {\n             "message": err.getErrorText()
  response.otpmethods = otpMethods.split(\";\");\n          };}\n\n        context.respond(200, 'OK', 'application/json', JSON.stringify(response));\n    }   catch(err) {\n        if (!response.message) (err instanceof Java.type('java.lang.Throwable')))\n            throw  response.message = "Unknown error";err;\n            \n        context.trace.trace(\"Authentication failed\", err);\n        if (err instanceof contextJava.respondtype(401, 'Registration Failed', 'application/json', JSON.stringify(response));'dk.itp.security.passticket.PTException')) {\n            }var response else= {\n                throw\"code\": err;.getErrorCode(),\n         }     } } 
register();

Import API directly

If you prefer, you can create a new API from this API Version specification directly.

Code Block
languagejs
titleAPI Version JSON Specification
linenumberstrue
collapsetrue
{
  "implementation": {"Sandbox": {\"message\": err.getErrorText()\n            };\n      "": {"*": {    \n   "proxy": {},       "script": "var response = {\n  text: 'Sorry, this API operation has not yet been implemented.'\n};\ncontext.respond(200, 'OK', 'application/json', JSON.stringify(response));",
      "pipeline": {}, if (response.code == 15) { // Need OTP\n                var otpmethods = context.agent.getStateVariable(pSessionId, \"otpmethods\");\n       "type": "script"     }},   if  "/hello": {"get": {(otpmethods)\n          "proxy": {},       "script": "var response.otpmethods = {\n\t\"text\": \"a string\"\n};\ncontext.respond(200, 'OK', 'application/json', JSON.stringify(response)otpmethods.split(';');\n",       "pipeline": {},    }\n   "type": "script"     }},   if  "/auth/uidpw": {"post": {(!response.message)\n          "proxy": {},     response.message = \"script": "%{script}function auth() {\nUnknown error\";\n             var req = JSON.parse(context.getRequestBodyAsString());\n        \n    context.trace.trace(\"Authenticate called with userid: \" + req.userid);\n\n    tryrespond(401, 'Authentication Failed', 'application/json', JSON.stringify(response));\n        } else {\n        var   pSessionId =throw context.iderr;\n        }\n    }\n}\n\nauth();\n",
     context.agent.setStateVariable(pSessionId, \"require_otp\", \"true\", false);\npipeline": {},
        context.agent.logon(pSessionId, req.userid, req.credentials);\n"type": "script",
      "override": true
 \n   }},
    var isAuthenticated = context.agent.isLoggedOn(pSessionId);\n"/info": {"get": {
      "proxy": {},
\n        "script": "%{script}var responsepSessionId = {context.id;\n\nvar isAuthenticated = context.agent.isLoggedOn( pSessionId );\n\nvar response = {\n    // Is delayed logoff or not\n            \"is_delayed_logoff\": context.agent.isDelayedLogoff(pSessionId),\n        \t// User ID\n        \t\"user_id\": context.agent.getUser(pSessionId),\n        \t// Customer ID\n        \t\"customer_id\": isAuthenticated ? context.agent.getCustomerID(pSessionId) : null,\n        \t// Agreement ID\n        \t\"agreement_id\": isAuthenticated ? context.agent.getAgreementID(pSessionId) : null,\n        \t// Is internal user\n        \t\"internal\": isAuthenticated ? context.agent.isInternalUser(pSessionId) : null,\n        \t// Is logged on\n        \t\"logged_on\": isAuthenticated,\n        \t// Users real name\n        \t\"user_name\": context.agent.getUserName(pSessionId),\n        \t// IP address that created the session\n        \t\"ip\": context.agent.getUserIP(pSessionId),\n        \t// Authentication level\n        \t\"authentication_level\": context.agent.getAuthenticationLevel(pSessionId),\n        \t// Authentication method\n        \t\"authentication_method\": context.agent.getAuthenticationMethod(pSessionId),\n        \t// Array of users groups\n        \t\"groups\": null,\n        \t// Name of creating agent\n        \t\"creating_agent\": context.agent.getNameOfCreatingAgent(pSessionId),\n        \t// Ticket or token\n        \t\"ticket\": context.agent.getTicketFromSession(pSessionId)\n};\n\ntry {\n    response.groups = Java.from(context.agent.getUserGroups(pSessionId));\n} catch(e) {}\n\ncontext.respond(200, 'OK', 'application/json', JSON.stringify(response));\n",
       \t// Available OTP methods\n    "pipeline": {},
      \t\"otpmethods\type": []\n"script",
       };\n"override": true
    }},
  \n  "/auth/logoff": {"put": {
   try {\n  "proxy": {},
        response.groups = Java.from(context.agent.getUserGroups(pSessionId)"script": "%{script}context.agent.logoff(context.id);\ncontext.respond(201, 'OK', 'application/json', '');\n",
      "pipeline": {},
catch(e) {}\n     "type": "script",
 \n     "override": true
 var otpMethods = context.agent.getStateVariable(pSessionId, \"otpmethods\");\n   }},
    "/auth/sendsms": {"put": {
     if (otpMethods) "proxy": {\n},
      "script": "%{script}try {\n    response.otpmethods = otpMethods.split(\";context.agent.newToken(context.id,43,\"sendsms\");\n        }\n\n        context.respond(200201, 'OKSMS Sent', 'application/json', JSON.stringify(response))'');\n    } catch(err) {\n        if (! (err instanceof Java.type('java.lang.Throwable')))\n            throw err;\n            \n        context.trace.trace(\"AuthenticationSendSMS failed\", err);\n        if (err instanceof Java.type('dk.itp.security.passticket.PTException')) {\n            var response = {\n                \"code\": err.getErrorCode(),\n                \"message\": err.getErrorText()\n            };\n            \n            if (response.code == 15) { // Need OTP\n                var otpmethods = context.agent.getStateVariable(pSessionId, \"otpmethods\");\n                if (otpmethods)\n                    response.otpmethods = otpmethods.split(';');\": err.getErrorText()\n        };\n    }\n    \n        if (!response.message)\n                response.message = \"Unknown error\";\n                \n            context.respond(401400, 'AuthenticationSend SMS Failed', 'application/json', JSON.stringify(response));\n    \n    } else {\n            throw err;\n        }\n    }\n}\n\nauth();\n",
      "pipeline": {},
      "type": "script",
      "override": true
    }},
    "/auth/infoverifysmsotp": {"getpost": {
      "proxy": {},
      "script": "%{script}var pSessionId = context.id;\n\nvar isAuthenticated = context.agent.isLoggedOn( pSessionId );\n\nvar response = {\n    // Is delayed logoff or not\n    \"is_delayed_logoff\": context.agent.isDelayedLogoff(pSessionId),\n\t// User ID\n\t\"user_id\": context.agent.getUser(pSessionId),\n\t// Customer ID\n\t\"customer_id\": isAuthenticated ? context.agent.getCustomerID(pSessionId) : null,\n\t// Agreement ID\n\t\"agreement_id\": isAuthenticated ? context.agent.getAgreementID(pSessionId) : null,\n\t// Is internal user\n\t\"internal\": isAuthenticated ? context.agent.isInternalUser(pSessionId) : null,\n\t// Is logged on\n\t\"logged_on\": isAuthenticated,\n\t// Users real name\n\t\"user_name\": context.agent.getUserName(pSessionId),\n\t// IP address that created the session\n\t\"ip\": context.agent.getUserIP(pSessionId),\n\t// Authentication level\n\t\"authentication_level\": context.agent.getAuthenticationLevel(pSessionId),\n\t// Authentication method\n\t\"authentication_method\": context.agent.getAuthenticationMethod(pSessionId),\n\t// Array of users groups\n\t\"groups\": null,\n\t// Name of creating agent\n\t\"creating_agent\": context.agent.getNameOfCreatingAgent(pSessionId),\n\t// Ticket or token\n\t\"ticket\": context.agent.getTicketFromSession(pSessionId)\n};\n\ntry {\n    response.groups = Java.from(context.agent.getUserGroups(pSessionId));\n} catch(e) {}\n\ncontext.respond(200, 'OK', 'application/json', JSON.stringify(response));\n",
      "pipeline": {},
      "type": "script",
      "override": true
    }},
    "/auth/logoff": {"put": {
      "proxy": {},
      "script": "%{script}context.agent.logoff(context.id);\ncontext.respond(201, 'OK"%{script}try {\n    var credentials = [pCode];\n    \n    context.agent.logon(context.id,43,context.agent.getUser(context.id), Java.to(credentials, \"java.lang.String[]\"));\n    \n    context.respond(201, 'OK', 'application/json', '');\n} catch(err) {\n    if (! (err instanceof Java.type('java.lang.Throwable')))\n        throw err;\n        \n    context.trace.trace(\"VerifySMS failed\", err);\n    if (err instanceof Java.type('dk.itp.security.passticket.PTException')) {\n        var response = {\n            \"code\": err.getErrorCode(),\n            \"message\": err.getErrorText()\n        };\n        \n        if (!response.message)\n            response.message = \"Unknown error\";\n            \n        context.respond(400, 'Verify SMS OTP Failed', 'application/json', '');JSON.stringify(response));    \n    } else {\n        throw err;\n    }\n}\n",
      "pipeline": {},
      "type": "script",
      "override": true
    }},
    "/auth/sendsmsverifytotp": {"putpost": {
      "proxy": {},
      "script": "%{script}try {\n    var credentials = [pCode];\n    \n    context.agent.newTokenlogon(context.id,45,context.agent.getUser(context.id,43,\"sendsms\"), Java.to(credentials, \"java.lang.String[]\"));\n    \n    context.respond(201, 'SMS SentOK', 'application/json', '');\n} catch(err) {\n    if (! (err instanceof Java.type('java.lang.Throwable')))\n        throw err;\n        \n    context.trace.trace(\"SendSMSVerify TOTP failed\", err);\n    if (err instanceof Java.type('dk.itp.security.passticket.PTException')) {\n        var response = {\n            \"code\": err.getErrorCode(),\n            \"message\": err.getErrorText()\n        };\n        \n        if (!response.message)\n            response.message = \"Unknown error\";\n            \n        context.respond(400, 'SendVerify SMSTOTP Failed', 'application/json', JSON.stringify(response));    \n    } else {\n        throw err;\n    }\n}\n",
      "pipeline": {},
      "type": "script",
      "override": true
    }},
    "/auth/verifysmsotpverifyemailotp": {"post": {
      "proxy": {},
      "script": "%{script}try {\n    var credentials = [pCode];\n    \n    context.agent.logon(context.id,4350,context.agent.getUser(context.id), Java.to(credentials, \"java.lang.String[]\"));\n    \n    context.respond(201, 'OK', 'application/json', '');\n} catch(err) {\n    if (! (err instanceof Java.type('java.lang.Throwable')))\n        throw err;\n        \n    context.trace.trace(\"VerifySMSVerify email OTP failed\", err);\n    if (err instanceof Java.type('dk.itp.security.passticket.PTException')) {\n        var response = {\n            \"code\": err.getErrorCode(),\n            \"message\": err.getErrorText()\n        };\n        \n        if (!response.message)\n            response.message = \"Unknown error\";\n            \n        context.respond(400, 'Verify SMSemail OTP Failed', 'application/json', JSON.stringify(response));    \n    } else {\n        throw err;\n    }\n}\n",
      "pipeline": {},
      "type": "script",
      "override": true
    }},
    "/auth/verifytotpsendemail": {"postput": {
      "proxy": {},
      "script": "%{script}try {\n    var credentials = [pCode];\n    \n    context.agent.logonnewToken(context.id,45,context.agent.getUser(context.id), Java.to(credentials, \"java.lang.String[]50,\"emailotp\"));\n    \n    context.respond(201, 'OKEmail Sent', 'application/json', '');\n} catch(err) {\n    if (! (err instanceof Java.type('java.lang.Throwable')))\n        throw err;\n        \n    context.trace.trace(\"VerifySend TOTPEmail failed\", err);\n    if (err instanceof Java.type('dk.itp.security.passticket.PTException')) {\n        var response = {\n            \"code\": err.getErrorCode(),\n            \"message\": err.getErrorText()\n        };\n        \n        if (!response.message)\n            response.message = \"Unknown error\";\n            \n        context.respond(400, 'VerifySend TOTPEmail Failed', 'application/json', JSON.stringify(response));    \n    } else {\n        throw err;\n    }\n}\n",
      "pipeline": {},
      "type": "script",
      "override": true
    }},
    "/auth/verifyemailotpresetpassword": {"post": {
      "proxy": {},
      "script": "%{script}try {\n    var credentialsbody = [pCode]context.getRequestBodyAsString();\n\ntry    {\n    context.agent.logonexecuteAuthpluginCommand(context.id,50,context.agent.getUser(context.id), Java.to(credentials, \"java.lang.String[]\") 9 , 'resetpassword', body);\n    \n    context.respond(201, 'OKPassword Reset', 'application/json', '');\n} catch(err) {\n    if (! (err instanceof Java.type('java.lang.Throwable')))\n        throw err;\n        \n    context.trace.trace(\"VerifyPassword email OTPreset failed\", err);\n    if (err instanceof Java.type('dk.itp.security.passticket.PTException')) {\n        var response = {\n            \"code\": err.getErrorCode(),\n            \"message\": err.getErrorText()\n        };\n        \n        if (!response.message)\n            response.message = \"Unknown error\";\n            \n        context.respond(400, 'VerifyPassword emailreset OTP Failedfailed', 'application/json', JSON.stringify(response));    \n    } else {\n        throw err;\n    }\n}\n",
      "pipeline": {},
      "type": "script",
      "override": true
    }},
    "/auth/sendemailinitiateresetpassword": {"putpost": {
      "proxy": {},
      "script": "%{script}tryvar body = context.getRequestBodyAsString();\n\ntry {\n    context.agent.newTokenexecuteAuthpluginCommand(context.id,50,\"emailotp\", 9 , 'initresetpassword', body);\n    \n    context.respond(201, 'EmailPassword SentReset', 'application/json', '');\n} catch(err) {\n    if (! (err instanceof Java.type('java.lang.Throwable')))\n        throw err;\n        \n    context.trace.trace(\"SendPassword Emailreset failed\", err);\n    if (err instanceof Java.type('dk.itp.security.passticket.PTException')) {\n        var response = {\n            \"code\": err.getErrorCode(),\n            \"message\": err.getErrorText()\n        };\n        \n        if (!response.message)\n            response.message = \"Unknown error\";\n            \n        context.respond(400, 'SendPassword Emailreset Failedfailed', 'application/json', JSON.stringify(response));    \n    } else {\n        throw err;\n    }\n}\n",
      "pipeline": {},
      "type": "script",
      "override": true
    }},
    "/resetpasswordresetpasswordotp": {"post": {
      "proxy": {},
      "script": "%{script}var body = context.getRequestBodyAsString();\n\ntry {\n    context.agent.executeAuthpluginCommand(context.id, 9 , 'resetpasswordresetpasswordotp', body);\n    \n    context.respond(201, 'Password Reset', 'application/json', '');\n} catch(err) {\n    if (! (err instanceof Java.type('java.lang.Throwable')))\n        throw err;\n        \n    context.trace.trace(\"Password reset failed\", err);\n    if (err instanceof Java.type('dk.itp.security.passticket.PTException')).passticket.PTException')) {\n        var response = {\n            \"code\": err.getErrorCode(),\n            \"message\": err.getErrorText()\n        };\n        \n        if (!response.message)\n            response.message = \"Unknown error\";\n        \n        context.respond(400, 'Password reset failed', 'application/json', JSON.stringify(response));    \n    } else {\n        var response = {\nthrow err;\n    }\n}\n",
      "pipeline": {},
      \"code\type": err.getErrorCode(),\n   "script",
      "override": true
    }},
    \"message\"/register": {"post": err.getErrorText()\n{
      "proxy": };\n{},
      "script": "%{script}function register() {\n    var req   if (!response.message)= JSON.parse(context.getRequestBodyAsString());\n    \n    context.trace.trace(\"Register called   response.message =with userid: \"Unknown error\"; + req.userid);\n\n    try    {\n        context.respond(400, 'Password reset failed', 'application/json', JSON.stringify(response));var pSessionId = context.id;\n    \n    var }CreateUserCredentials else {\n= Java.type(\"dk.itp.security.passticket.CreateUserCredentials\");\n        var cc = thrownew errCreateUserCredentials();\n    }\n}\n",    cc.userid =  "pipeline": {},req.userid;\n        cc.password  "type": "script",= req.password;\n        "override": true
cc.firstname = req.firstname;\n    }},     "/initiateresetpassword": {"post": {cc.lastname = req.lastname;\n        "proxy": {},
cc.email = req.email;\n      "script": "%{script}var body cc.mobile = contextreq.getRequestBodyAsString()mobile;\n\ntry {        \n        context.agent.executeAuthpluginCommandlogon(context.idpSessionId, 9 , 'initresetpassword'req.userid, bodycc);\n        \n        context.respond(201, 'PasswordRegistration Resetsuccessful', 'application/json', '');\n    } catch(err) {\n        if (! (err instanceof Java.type('java.lang.Throwable')))\n            throw err;\n            \n        context.trace.trace(\"Password resetRegistration failed\", err);\n        if (err instanceof Java.type('dk.itp.security.passticket.PTException')) {\n            var response = {\n                \"code\": err.getErrorCode(),\n                \"message\": err.getErrorText()\n            };\n             \n    \n        if (!response.message)\n                response.message = \"Unknown error\";\n                \n            context.respond(400401, 'PasswordRegistration reset failedFailed', 'application/json', JSON.stringify(response));\n    \n    } else {\n            throw err;\n        }\n    }\n}\n\nregister();\n",
      "pipeline": {},
      "type": "script",
      "override": true
    }},
    "/resetpasswordotptotp/qr": {"post": {
      "proxy": {},
      "script": "%{script}var body = context.getRequestBodyAsString();\n\ntry {\n    context.agent.executeAuthpluginCommand(context.id, 9 , 'resetpasswordotp', body);\n    \n    context.respond(201, 'Password Reset', 'application/json', '');\n} catch(err) {\n    if (! (err instanceof Java.type('java.lang.Throwable')))\n        throw err;\n        \n    context.trace.trace(\"Password reset failed\", err);try {\r\n    if (err instanceof Java.type('dk.itp.security.passticket.PTException'!context.agent.isLoggedOn(context.id)) {\r\n        var response = {\r\n            \"code\": err.getErrorCode(), 18, // AuthErrorCodes.ERROR_NOTAUTHENTICATED\r\n            \"message\": err.getErrorText()\n        };\n        \n        if (!response.message)\"Requires authentication\"\r\n            response.message = \"Unknown error\";};\r\n        \r\n        context.respond(400403, 'PasswordDenied not resetlogged failedon', 'application/json', JSON.stringify(response));    \r\n    } else {\r\n        var body throw err= JSON.parse(context.getRequestBodyAsString());\r\n    }\n}\r\n",       "pipeline": {},
   var response = {\r\n    "type": "script",   \t// QR Code in "override": true
    }},
    "/register": {"post": {PNG format\r\n         "proxy": {},\t// Format: base64\r\n        \t// Secret "script": "%{script}function register() {\nvalue to register\r\n      var req = JSON.parse(context.getRequestBodyAsString()); \t\"secret\": context.agent.newToken(context.id,45, \"secretpassword;\"+body.password),\r\n    \n    \t\"qr\": context.traceagent.trace(\"Register called with userid: \" + req.userid);\nnewToken(context.id,45, \"PNG\")\r\n    try {\n        var pSessionId = context.id;};\r\n        var CreateUserCredentials = Java.type(\"dk.itp.security.passticket.CreateUserCredentials\");\context.respond(200, 'OK', 'application/json', JSON.stringify(response));\r\n    }\r\n} catch(err) {\r\n   var if cc(! =(err newinstanceof CreateUserCredentials();Java.type('java.lang.Throwable')))\r\n        cc.userid = req.useridthrow err;\r\n        cc.password = req.password;\r\n    context.trace.trace(\"QR Code registration  cc.firstname = req.firstname;failed\", err);\r\n    if (err   cc.lastname = req.lastname;instanceof Java.type('dk.itp.security.passticket.PTException')) {\r\n        cc.emailvar response = req.email;{\r\n           cc.mobile = req.mobile; \"code\": err.getErrorCode(),\r\n        \n        context.agent.logon(pSessionId, 9, req.userid, cc);\"message\": err.getErrorText()\r\n        };\r\n        context.respond(201, 'Registration successful', 'application/json', '');\n    } catch(err) {\r\n        if (! (err instanceof Java.type('java.lang.Throwable')))\nresponse.message)\r\n            response.message  throw err= \"Unknown error\";\r\n            \r\n        context.trace.trace(\"Registration failed\", err);\nrespond(400, 'QR Code registration failed', 'application/json', JSON.stringify(response));    \r\n    if (err instanceof Java.type('dk.itp.security.passticket.PTException')) {\} else {\r\n        throw err;\r\n   var response = {\n }\r\n}\r\n",
        "pipeline": {},
      \"code\type": err.getErrorCode(),\n"script",
      "override": true
    }}
  \"message\}},
  "security": err.getErrorText()\n{
    "authorization": {
      };\n "noauthorization.for.options": false,
      "roles": []
  \n  },
    "authentication": {
   if (!response.message)\n  "apikey": false,
      "apikey.headername": "ceptor-apikey",
    response.message = \"Unknown error\";\n      basicauth": false,
      "clientcert": false,
 \n     "bearer": false,
     context.respond(401, 'Registration Failed', 'application/json', JSON.stringify(response));\n "oauth2": false,
      "openidconnect": false,
} else {\n    "oauth2.scopes": [],
      throw err;\n"advanced": {}
    }
  }\n,
   }\n}\n\nregister();\n"id": "9fc1faef-4085-47bd-befe-74eeb287ddf4",
  "apiid": "5be09f26-784a-4b2d-b75e-5bb82c11bc32",
  "pipelinename": {}"v1",
  "basepath": "/ceptorauthenticate/1/",
  "typeapitype": "scriptopenapi",
  "cors": true,
  "overrideprivate": truefalse,
    }}"openapispec": {
  }},   "securityopenapi": {"3.0.0",
    "authorizationinfo": {
      "noauthorization.for.options": falseversion": "1.0.0",
      "rolestitle": []"Ceptauthenticate"
    },
    "paths": {
      "/info": {"authenticationget": {
        "apikeysummary": false, "Gets info about currently authenticated user/session",
        "apikey.headernameoperationId": "ceptor-apikeyinfo",
        "basicauthresponses": {
   false,       "clientcert200": false,{
      "bearer": false,       "oauth2description": "Session falseInfo",
      "openidconnect": false,     "content": {"application/json": {"oauth2.scopesschema": [],
{"$ref": "#/components/schemas/Session"}}}
     "advanced": {}     },
  },    "id": "9fc1faef-4085-47bd-befe-74eeb287ddf4",   "apiiddefault": "5be09f26-784a-4b2d-b75e-5bb82c11bc32", {
    "name": "v1",   "basepath": "/ceptorauthenticate/1/",   "apitypedescription": "openapiunexpected error",
    "cors": true,   "private": false,   "openapispeccontent": {
    "openapi"application/json": {"3.0.0",
    "info": {schema": {"$ref": "#/components/schemas/Error"}}}
      "version": "1.0.0",   }
   "title": "CeptorAuthenticate"    }
},     "paths": { }},
      "/auth/infouidpw": {"getpost": {
        "summaryrequestBody": {"Gets info about currently authenticated user/session"content": {"application/json": {"schema": {"$ref": "#/components/schemas/AuthenticateRequest"}}}},
        "operationIdsummary": "infoAuthenticate using userid/password",
        "responses": {
          "200201": {
            "description": "SessionAuthentication Infosuccessful",
            "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Session"}}}
          },
          "default": {
            "description": "unexpected error",
            "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
          }
        },
        "description": "Authenticate a user using userid and password",
        "operationId": "authuidpw",
        "deprecated": false,
        "tags": []
      }},
      "/auth/uidpwlogoff": {"postput": {
        "requestBodysummary": {"content": {"application/jsonLogoff",
        "description": {"schema": {"$refLogoff the user",
        "operationId": "#/components/schemas/AuthenticateRequest"}}}}logoff",
        "summaryresponses": "Authenticate{
using userid/password",         "responses201": {"description": "Logoff completed"},
          "201default": {
            "description": "Authenticationunexpected successfulerror",
            "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SessionError"}}}
          }
        },
      }},
      "/auth/sendsms": {"defaultput": {
        "summary": "Send SMS",
        "description": "unexpected error",Send SMS to the user",
        "operationId": "sendsms",
        "contentresponses": {"application/json
          "201": {"schemadescription": {"$ref": "#/components/schemas/Error"}}}SMS sent"},
          }
    "default": {
   },         "description": "Authenticate a user using userid and password","unexpected error",
            "content": {"application/json": {"schema": {"operationId$ref": "authuidpw",#/components/schemas/Error"}}}
        "deprecated": false, }
       "tags": [] }
      }},
      "/auth/logoffsendemail": {"put": {
        "summary": "LogoffSend Email",
        "description": "Logoff": "Send OTP via email to the user",
        "operationId": "logoffsendemail",
        "responses": {
          "201": {"description": "LogoffEmail completedsent"},
          "default": {
            "description": "unexpected error",
            "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
          }
        }
      }},
      "/auth/sendsmsverifysmsotp": {"putpost": {
        "summary": "SendVerify SMS OTP",
        "description": "Send SMS to the userVerifies OTP code earlier sent as SMS",
        "operationId": "sendsmsverifysms",
        "responses": {
          "201": {"description": "SMS sentOK"},
          "default": {
            "description": "unexpected error",
            "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
          }
        }
   ,
  }},       "/auth/sendemailparameters": [{"put":
 {         "summaryname": "Send Emailcode",
          "description": "SendEntered OTP via email to the user",code",
          "operationIdin": "sendemailquery",
        "responses": {
          "201required": {"description": "Email sent"}true,
          "defaultallowEmptyValue": {
 false,
          "descriptiondeprecated": "unexpected error",
  false,
         "content": {"application/json": {"schema": {"$reftype": "#/components/schemas/Error"}}}string"}
          }
        }}]
      }},
      "/auth/verifysmsotpverifytotp": {"post": {
        "summary": "Verify SMS OTPTOTP",
        "description": "Verifies OTPTOTP code earlier sent as SMS(Google/MS etc. Authenticator App)",
        "operationId": "verifysmsverifytotp",
        "responses": {
          "201": {"description": "OK"},
          "default": {
            "description": "unexpected error",
            "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
          }
        },
        "parameters": [{
          "name": "code",
          "description": "Entered OTP code",
          "in": "query",
          "required": true,
          "allowEmptyValue": false,
          "deprecated": false,
          "schema": {"type": "string"}
        }]
      }},
      "/auth/verifytotpverifyemailotp": {"post": {
        "summary": "Verify TOTPemail OTP",
        "description": "Verifies TOTPOTP code (Google/MS etc. Authenticator App)sent via email",
        "operationId": "verifytotpverifyemailotp",
        "responses": {
          "201": {"description": "OK"},
          "default": {
            "description": "unexpected error",
            "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
          }
        },
        "parameters": [{
          "name": "code",
          "description": "Entered OTP code",
          "in": "query",
          "required": true,
          "allowEmptyValue": false,
          "deprecated": false,
          "schema": {"type": "string"}
        }]
      }},
      "/auth/verifyemailotpinitiateresetpassword": {"post": {
        "summary": "VerifyInitiate emailReset OTPpassword",
        "description": "VerifiesInitiate Reset password, prompting for OTP code sent via- possibly sending challenge using e.g. SMS or email",
        "operationId": "verifyemailotpinit resetpassword",
        "responses": {
          "201": {"description": "OK"},
          "default": {
            "description": "unexpected error",
            "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
          }
        },
        "parametersrequestBody": [{
          "name"content": {"code",
          "description": "Entered OTP code",
          "in": "query",
          "required": true,
          "allowEmptyValue": false,
          "deprecated": false,
          "application/json": {"schema": {"type$ref": "string#/components/schemas/InitiateResetPasswordRequest"}
        }]
      }},
 
    "/initiateresetpassword": {"post": {
        "summary": "Initiate Reset password",  }},
      "description/resetpasswordotp": {"post"Initiate: Reset{
password, prompting for OTP code - possibly sending challenge using e.g. SMS or email"summary": "Reset password - verify OTP",
        "operationIddescription": "initVerify OTP resetpasswordcode",
        "responses": {
          "201": {"description": "OK"},
          "default": {
            "description": "unexpected error",
            "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
          }
        },
        "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/InitiateResetPasswordRequestResetPasswordOTPRequest"}}}}
      }},
      "/resetpasswordotpresetpassword": {"post": {
        "summary": "Reset password",
        "description": "Resets your password, -by verifyanswering OTP challenge",
        "descriptionoperationId": "Verify OTP coderesetpassword",
        "responses": {
          "201": {"description": "OK"},
          "default": {
            "description": "unexpected error",
            "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
          }
        },
        "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ResetPasswordOTPRequestResetPasswordRequest"}}}}
      }},
      "/resetpasswordregister": {"post": {
        "summary": "ResetRegister passworduser",
        "description": "Resets your password, by answering OTP challengeSelf registration",
        "operationId": "resetpasswordregister",
        "responses": {
          "201": {"description": "OK"},
          "default": {
            "description": "unexpected error",
            "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
          }
        },
        "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ResetPasswordRequestRegisterRequest"}}}}
      }},
      "/totp/registerqr": {"post": {
        "summary": "Register userGenerate TOTP secret and QR code",
        "descriptionoperationId": "Self registrationtotpqr",
        "operationIdresponses": {
          "register",200": {
            "responsesdescription": { "TOTP QR code",
            "content": "201{"application/json": {"schema": {"description$ref": "OK"#/components/schemas/TOTPQRResponse"}}}
          },
          "default": {
            "description": "unexpected error",
            "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
          }
        },
        "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/RegisterRequestTOTPQRRequest"}}}}
      }}
    },
    "components": {
      : {"schemas": {
 
      "AuthenticateRequest": {

         "required": ["userid"],
          "properties": {

           "userid": {"type": "string"},
            "credentials": {"type": "string"}
          }
 
      },
        "Session": {"properties": {
 
        "is_delayed_logoff": {
 
          "type": "boolean",

           "description": "True, if delayed logoff is initiated for this session"
          },
          "user_id": {
 
          "type": "string",
            "description": "User ID"
 
        },
 
        "customer_id": {

           "type": "string",
            "description": "Customer ID"
 
        },
 
        "agreement_id": {
            "type": "string",
 
          "description": "Agreement ID"
 
        },

         "logged_on": {
            "type": "boolean",
 
          "description": "Indicates if a user is authenticated in this session"
 
        },
 
        "internal": {
 
          "type": "boolean",
            "description": "Is this user an internal user or not"
 
        },
          "user_name": {
            "type": "string",
            "description": "Users real name"
        },
        "ip": {
          "type": "string",
          "description": "IP address that created the session"
        },
        "groups": {
          "type": "array",
          "ipdescription": "Array of users groups",
          "items": {
            "type": "string",
            "description": "Group/Role name"IP
    address that created the session"  }
        },
 
        "groupsauthentication_method": {

           "type": "arrayinteger",

           "description": "Array of users groups",Authentication method identifier"
        },
        "itemsauthentication_level": {
   
          "type": "stringinteger",

             "description": "Group/Role name"
            }Authentication level - the highger, the more secure"
          },

         "authenticationcreating_methodagent": {
 
          "type": "integerstring",
 
          "description": "Authentication method identifier"
 Name of agent that created this session"
        },
 
        "authentication_levelticket": {
 
          "type": "integerstring",
            "description": "Authentication level - the highger, the more secureOptional ticket for this session - can be JWT token, SAML token, SSL clientcert or anything else used as an alternate key to this session instead of the session ID"
        }
  },    }},
      "creating_agentResetPasswordRequest": {
   
        "typedescription": "string"Reset password input, OTP must be verified first, or this call will fail",
  "description": "Name of agent that created this session""required": [
          }"userid",
          "ticket": {newpassword"
        ],
        "typeproperties": "string",
{
           "descriptionuserid": "Optional{
ticket for this session - can be JWT token, SAML token, SSL clientcert or anything else used as an alternate key to this session instead of the session ID"description": "Userid",
            "type": "string"
          },
        }},
        "ResetPasswordRequest"newpassword": {
            "description": "ResetNew password input",
OTP must be verified first, or this call will fail",   "type": "string"
      "required": [   }
        }
"userid",      },
      "newpasswordResetPasswordOTPRequest": {
         ],
 "description": "Reset password OTP input",
        "propertiesrequired": {
[
           "userid": {,
          "email",
         "description": "Useridotptype",
          "code"
   "type": "string"    ],
        },
 "properties": {
          "newpassworduserid": {
 
            "description": "New passwordUserid",
              "type": "string"

           }
    ,
     }         },
        "ResetPasswordOTPRequest"email": {
            "description": "Reset password OTP input",Email address - must match existing record",
            "requiredtype": ["string"
          },
 "userid",         "otptype": {
  "email",          "description": "Type of otp"otptype",
            "code"enum": [
              ],"totp",
              "propertiesemail":,
 {             "useridsms":
{               "description": "Userid",
],
             "type": "string"
            },
 
          "emailcode": {
              "description": "Email address - must match existing recordOTP code",
 
            "type": "string"
          }
   },     }
      },
"otptype": {     "InitiateResetPasswordRequest": {
        "description": "TypeInitiate ofReset otp",password input - used when starting a request to ask for password reset, e.g.  "enum": [
 to OTP code to be submitted",
        "required": [
    "totp",      "userid",
          "email",
     
          "smsotptype"
        ],
     ],   "properties": {
          "typeuserid": "string"{
             }"description": "Userid",
            "codetype": { "string"
          },
          "descriptionemail": "OTP code",
 {
            "typedescription": "string"Email address - must match existing record",
      }         "type": "string"
 }         },
          "InitiateResetPasswordRequestotptype": {
            "description": "InitiateType Reset password input - used when starting a request to ask for password reset, e.g. to OTP code to be submitted",
 of otp",
               "requiredenum": [
              "useridtotp",
              "email",
              "otptypesms"
            ],
            "propertiestype": {"string"
          }
 "userid": {      }
        "description": "Userid",},
      "RegisterRequest": {
        "typedescription": "stringRegistration form",
        "required": [
   },       "firstname",
     "email": {    "lastname",
          "descriptionuserid":,
"Email address - must match existing record",    "password",
          "typeemail":,
"string"          "mobile"
  },      ],
        "otptypeproperties": {
 
            "descriptionfirstname": "Type{
of otp",               "enumdescription": [
   "First name",
            "totp",
         type": "string"
      "email",      },
          "smslastname": {
             ]"description": "Last name",
              "type": "string"
            },
          }"userid": {
       },        "description": "RegisterRequestUserid":,
 {           "descriptiontype": "Registration formstring",
          "required": [},
          "password": {
 "firstname",            "description": "lastnamePassword",
            "useridtype",: "string"
           "password",
 },
          "email",: {
            "description": "mobileEmail address",
          ],  "type": "string"
       "properties": {  },
          "firstnamemobile": {
              "description": "FirstMobile namenumber",
              "type": "string"
          }
  },      }
      "lastname": {},
      "TOTPQRRequest": {
        "descriptionrequired": ["Last namepassword"],
     
        "typeproperties": {"stringpassword": {
           }"type": "string",
            "useridformat": {
   "password",
          "description": "UseridUser password",
        }}
     "type": "string" },
      "TOTPQRResponse": {
    },    "required":  [
      "password": {    "qr",
          "descriptionsecret":
"Password",        ],
        "typeproperties": "string"{
          "qr": {
},
            "emailtype": {
"string",
             "descriptionformat": "Email addressbase64",
              "typedescription": "string"
 QR Code in PNG format"
          },
 
          "mobilesecret": {
              "descriptiontype": "Mobile numberstring",
              "typedescription": "string"Secret value to register"
          }
          }
        },

       "Error": {
          "required": [
 
          "code",
 
          "message"
          ],
 
        "properties": {
 
          "code": {

             "type": "integer",
              "format": "int32"
 
          },
 
          "message": {"type": "string"}
          }
        }
      },
 
    "securitySchemes": {}
    },
    "security": [],
    "servers": [{
      "url": "https://localhost:8443/ceptorauthenticate/1",
      "description": "Sandbox environment, used for initial testing of APIs, playing around with new versions"
    }]
  },
  "tags": [],
  "requestmodification": {
    "session.needed": false,
    "cookiesnapper": {},
    "plugin": {}
  },
  "deployed": ["Sandbox"],
  "override.apiprofile.security": true,
  "remote.openapispec.loadfromdestination": false,
  "deprecated": false,
  "documentation": "<h1>CeptorAuthenticate<\/h1>\n\n<p>This API is used by html/javascript clients to authenticate a user to Ceptor using MultiFactor Authentication.<\/p>\n\n<p>Please refer to documentation at <a href=\"https://docs.ceptor.io\">https://docs.ceptor.io<\/a><\/p>\n"
}