...
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
/** * 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 blue yellow 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".
Set conditions to match /auth/*
and create an URL rewrite rule, which rewrites /auth/(.*) to /$1 - effectively stripping the /auth/ prefix from the request.
Select the WebServer section, and point the webserver at ${ceptor.home}/auth where the html / javascript application is then served from.
Below is a location configuration which can be copied into the gateway configuration, with the configuration described above.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
{
"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 | ||||||
---|---|---|---|---|---|---|
| ||||||
--- 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: 200Ceptor.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".
Set conditions to match /auth/*
and create an URL rewrite rule, which rewrites /auth/(.*) to /$1 - effectively stripping the /auth/ prefix from the request.
Select the WebServer section, and point the webserver at ${ceptor.home}/auth where the html / javascript application is then served from.
Below is a location configuration which can be copied into the gateway configuration, with the configuration described above.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
{
"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 | ||||||
---|---|---|---|---|---|---|
| ||||||
--- 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: "Sessionunexpected Infoerror" content: application/json: schema: $ref: "#/components/schemas/Session"Error" requestBody: defaultcontent: descriptionapplication/json: "unexpected error" contentschema: application/json$ref: "#/components/schemas/ResetPasswordOTPRequest" /resetpassword: post: summary: "Reset password" schemadescription: "Resets your password, by answering OTP challenge" operationId: "resetpassword" $ref: "#/components/schemas/Error" /auth/uidpw: responses: "201": description: "OK" post: default: requestBodydescription: "unexpected error" content: application/json: schema: $ref: "#/components/schemas/AuthenticateRequestError" requestBody: summary: "Authenticate using userid/password" content: application/json: responses: schema: 201: description$ref: "Authentication successful#/components/schemas/ResetPasswordRequest" /register: post: content: summary: "Register user" application/json:description: "Self registration" operationId: "register" schemaresponses: "201": $refdescription: "#/components/schemas/SessionOK" default: description: "unexpected error" content: application/json: schema: $ref: "#/components/schemas/Error" descriptionrequestBody: "Authenticate a user using userid and password" content: operationId: "authuidpw" deprecatedapplication/json: false tags: [] /auth/logoff: putschema: summary: "Logoff" description$ref: "Logoff the user" "#/components/schemas/RegisterRequest" /totp/qr: operationIdpost: "logoff" summary: responses:"Generate TOTP secret and QR code" 201: operationId: "totpqr" descriptionresponses: "Logoff completed" default"200": description: "unexpectedTOTP QR errorcode" content: application/json: schema: $ref: "#/components/schemas/ErrorTOTPQRResponse" /auth/sendsms: putdefault: summary description: "Sendunexpected SMSerror" description: "Send SMS to thecontent: user" operationId: "sendsms" application/json: responses: 201: schema: description: "SMS sent" default$ref: "#/components/schemas/Error" descriptionrequestBody: "unexpected error" content: application/json: schema: $ref: "#/components/schemas/ErrorTOTPQRRequest" /auth/sendemailcomponents: putschemas: summary: "Send Email"AuthenticateRequest: descriptionrequired: "Send OTP via email to the user" operationId: "sendemail - "userid" responsesproperties: 201userid: descriptiontype: "Email sentstring" defaultcredentials: descriptiontype: "unexpectedstring" error" Session: contentproperties: is_delayed_logoff: application/json: type: "boolean" schema: description: "True, if delayed logoff is initiated for this session" $ref: "#/components/schemas/Error" /auth/verifysmsotpuser_id: post: summarytype: "string"Verify SMS OTP" description: "VerifiesUser OTPID" code earlier sent as SMS" customer_id: operationId: "verifysms" responsestype: "string" 201: description: "OKCustomer ID" defaultagreement_id: descriptiontype: "unexpected errorstring" contentdescription: "Agreement ID" logged_on: application/json: type: "boolean" schema: description: "Indicates if a user is authenticated in this session" $ref: "#/components/schemas/Error" internal: parameters: - nametype: "codeboolean" description: "EnteredIs OTPthis code"user an internal user or not" in: "query" user_name: required: true type: "string" allowEmptyValue: false description: "Users deprecated:real falsename" schemaip: type: "string" /auth/verifytotp: post: description: "IP address that summary:created "Verifythe TOTPsession" description: "Verifies TOTP code (Google/MS etc. Authenticator App)" groups: operationIdtype: "verifytotparray" responses description: "Array of users groups" 201: items: description: "OK" type: "string" default: description: "unexpectedGroup/Role errorname" content:authentication_method: type: "integer" application/json: description: "Authentication method identifier" schema: authentication_level: $reftype: "#/components/schemas/Errorinteger" parameters: description: "Authentication level - name: "code the highger, the more secure" descriptioncreating_agent: "Entered OTP code" intype: "querystring" required: true description: "Name of agent that created this session" allowEmptyValue: false ticket: deprecated: false schema:type: "string" typedescription: "string"Optional ticket /auth/verifyemailotp: post: for this session - can be JWT token, SAML\ summary: "Verify email OTP" \ token, SSL description:clientcert "Verifiesor OTPanything codeelse sentused viaas email"an alternate key to this\ operationId: "verifyemailotp" responses: \ session instead of the session ID" 201: ResetPasswordRequest: description: "OK"Reset password input, OTP must be verified first, or default:this call\ \ description:will fail"unexpected error" required: content: - "userid" - application/json:"newpassword" properties: schemauserid: description: "Userid" $ref: "#/components/schemas/Error" type: "string" parameters: - namenewpassword: "code" description: "EnteredNew password" OTP code" intype: "querystring" requiredResetPasswordOTPRequest: true description: "Reset password allowEmptyValue:OTP falseinput" required: deprecated: false - "userid" schema: - "email" type:- "stringotptype" /initiateresetpassword: - post:"code" summaryproperties: "Initiate Reset password" descriptionuserid: "Initiate Reset password, prompting for OTP code - possibly sending\ description: "Userid" \ challenge using e.g. SMS or email" type: "string" operationIdemail: "init resetpassword" responses: description: "Email address - must match existing 201:record" descriptiontype: "OKstring" defaultotptype: description: "unexpectedType of errorotp" contentenum: - "totp" application/json: - "email" schema: - "sms" $reftype: "#/components/schemas/Errorstring" requestBody code: content: description: "OTP code" application/json: type: "string" InitiateResetPasswordRequest: schema: description: "Initiate Reset password input - used when starting a $ref: "#/components/schemas/InitiateResetPasswordRequest"request to\ /resetpasswordotp: post: \ ask for password reset, summary: "Reset password - verify OTPe.g. to OTP code to be submitted" descriptionrequired: "Verify OTP code" - "userid" responses: - "email" 201: - "otptype" descriptionproperties: "OK" defaultuserid: description: "unexpected errorUserid" contenttype: "string" application/jsonemail: description: "Email address - schema:must match existing record" $reftype: "#/components/schemas/Error"string" requestBodyotptype: content: description: "Type of otp" application/json: enum: schema: - "totp" $ref:- "#/components/schemas/ResetPasswordOTPRequestemail" /resetpassword: post: - "sms" summary: "Reset password" descriptiontype: "string"Resets your password, by answering OTP challenge"RegisterRequest: operationIddescription: "resetpasswordRegistration form" responsesrequired: - "firstname" 201: - "lastname" description: "OK" - "userid" default: - "password" description:- "unexpected erroremail" - "mobile" content: properties: application/jsonfirstname: description: "First name" schema: type: "string" $reflastname: "#/components/schemas/Error" requestBody: description: "Last name" content: type: "string" application/json: userid: schema: description: "Userid" $reftype: "#/components/schemas/ResetPasswordRequeststring" /register: postpassword: summary: "Register user" description: "Password" description: "Self registration" operationIdtype: "registerstring" responsesemail: 201: description: "Email address" descriptiontype: "OKstring" defaultmobile: description: "unexpectedMobile errornumber" contenttype: "string" TOTPQRRequest: application/jsonrequired: - "password" schemaproperties: password: $ref type: "#/components/schemas/Errorstring" requestBody: format: "password" content: description: "User password" application/json: TOTPQRResponse: required: schema: - "qr" - "secret" $ref: "#/components/schemas/RegisterRequest" components: schemasproperties: AuthenticateRequest: qr: required: -type: "useridstring" properties: format: "base64" userid: description: "QR Code in type:PNG format"string" credentialssecret: type: "string" Session: propertiesdescription: "Secret value to register" is_delayed_logoffError: required: type: "boolean" - "code" - description: "True, if delayed logoff is initiated for this session""message" properties: user_idcode: type: "stringinteger" descriptionformat: "User IDint32" customer_idmessage: type: "string" security: [] servers: - url: "https://localhost:8443/ceptorauthenticate/1" description: "CustomerSandbox ID"environment, used for initial testing of APIs, playing around\ agreement_id: \ with new 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: "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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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 groups: type: "array" description: "level "authentication_level": context.agent.getAuthenticationLevel(pSessionId), // Authentication method "authentication_method": context.agent.getAuthenticationMethod(pSessionId), // Array of users groups" items: type: "string" description: "Group/Role name" authentication_method: type: "integer" description: "Authentication method identifier" "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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
function auth() { var req = JSON.parse(context.getRequestBodyAsString()); authentication_level: context.trace.trace("Authenticate called typewith userid: "integer" + req.userid); try { description: "Authentication level - the highger, the morevar secure"pSessionId = context.id; creating_agent: type:context.agent.setStateVariable(pSessionId, "stringrequire_otp", "true", false); description: "Name of agent that created this session"context.agent.logon(pSessionId, req.userid, req.credentials); ticket: var isAuthenticated = type: "string"context.agent.isLoggedOn(pSessionId); description: "Optional ticket for this sessionvar -response can= be{ JWT token, SAML\ // Is delayed \ token, SSL clientcert logoff or anythingnot else used as an alternate key to this\ \ session instead of the session ID" ResetPasswordRequest:"is_delayed_logoff": context.agent.isDelayedLogoff(pSessionId), // User ID description: "Reset password input, OTP must be verified first, or this call\ user_id": context.agent.getUser(pSessionId), // Customer ID \ will fail" "customer_id": isAuthenticated ? context.agent.getCustomerID(pSessionId) : null, required: // Agreement -ID "userid" - "newpasswordagreement_id": isAuthenticated ? context.agent.getAgreementID(pSessionId) : null, properties: // Is userid:internal user description "internal": "Userid"isAuthenticated ? context.agent.isInternalUser(pSessionId) : null, type: "string" // Is logged on newpassword: "logged_on": isAuthenticated, description: "New password" // Users real name type: "stringuser_name": context.agent.getUserName(pSessionId), ResetPasswordOTPRequest: // IP description:address "Resetthat passwordcreated OTPthe input"session required: - "userid" "ip": context.agent.getUserIP(pSessionId), - "email" // Authentication level - "otptype" "authentication_level": context.agent.getAuthenticationLevel(pSessionId), - "code" // Authentication properties:method userid "authentication_method": context.agent.getAuthenticationMethod(pSessionId), // description: "Userid" Array of users groups type: "stringgroups": null, email: // Name of creating agent description: "creating_agent"Email address - must match existing record": context.agent.getNameOfCreatingAgent(pSessionId), // Ticket or token type: "string" "ticket": context.agent.getTicketFromSession(pSessionId), otptype: // Available OTP methods description: "Type of otp" "otpmethods": [] enum: }; - "totp" try { - "email" response.groups - "sms"= Java.from(context.agent.getUserGroups(pSessionId)); } type: "string"catch(e) {} code: var otpMethods description: "OTP code"= context.agent.getStateVariable(pSessionId, "otpmethods"); if (otpMethods) { type: "string" InitiateResetPasswordRequest: response.otpmethods description: "Initiate Reset password input - used when starting a request to\= otpMethods.split(";"); } context.respond(200, 'OK', 'application/json', \ ask for password reset, e.g. to OTP code to be submitted"JSON.stringify(response)); } catch(err) { if (! (err required: instanceof Java.type('java.lang.Throwable'))) - "userid" throw -err; "email" - "otptype" properties: context.trace.trace("Authentication failed", err); userid: if (err description: "Userid"instanceof Java.type('dk.itp.security.passticket.PTException')) { type: "string" var response = { email: description: "code"Email address - must match existing record": err.getErrorCode(), type: "stringmessage": err.getErrorText() otptype: }; description: "Type of otp" enum: if (response.code == - "totp" 15) { // Need OTP - "email" var otpmethods -= context.agent.getStateVariable(pSessionId, "smsotpmethods"); type: "string" RegisterRequest:if (otpmethods) description: "Registration form" required: response.otpmethods - "firstname"= otpmethods.split(';'); - "lastname" } - "userid" - "password" if (!response.message) - "email" - "mobile" response.message = properties:"Unknown error"; firstname: description: "First name" context.respond(401, 'Authentication type: "string"Failed', 'application/json', JSON.stringify(response)); lastname:} else { description: "Last name" throw err; type: "string"} } } userid: description: "Userid"auth(); |
POST /auth/logoff
This operation simply logs off the user.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
try { context.agent.newToken(context.id,43,"sendsms"); type: "string" context.respond(201, 'SMS Sent', 'application/json',''); } catch(err) { password:if (! (err instanceof Java.type('java.lang.Throwable'))) description: "Password" throw err; type: "string" context.trace.trace("SendSMS failed", err); email: if (err instanceof description: "Email address"Java.type('dk.itp.security.passticket.PTException')) { type: "string" mobile:var response = { description"code": "Mobile number"err.getErrorCode(), type: "stringmessage": err.getErrorText() Error: }; required: - "code" - "message"if (!response.message) properties: response.message = "Unknown error"; code: type: "integer" context.respond(400, 'Send format: "int32" SMS Failed', 'application/json', JSON.stringify(response)); message: } else { 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
...
throw err;
}
} |
POST /auth/sendemail
This operation generates an OTP and sends it using email to the users email address.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
vartry { pSessionId = context.agent.newToken(context.id,50,"emailotp"); context.respond(201, 'Email Sent', 'application/json',''); } catch(err) { if (! (err instanceof Java.type('java.lang.Throwable'))) throw err; var isAuthenticated = context.agent.isLoggedOn( pSessionId );.trace.trace("Send Email failed", err); if (err instanceof Java.type('dk.itp.security.passticket.PTException')) { var response = { // Is delayed logoff or not "is_delayed_logoffcode": contexterr.agent.isDelayedLogoffgetErrorCode(pSessionId), // User ID "user_id "message": contexterr.agent.getUsergetErrorText(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 }; 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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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', 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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
function auth() { var req = JSON.parse(context.getRequestBodyAsString()) ''); } 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(pSessionId400, "require_otp", "true", false); 'Verify SMS OTP Failed', 'application/json', JSON.stringify(response)); } else { throw err; context.agent.logon(pSessionId, req.userid, req.credentials); } } |
POST /auth/verifytotp
This operation is used for verifying a TOTP code entered using a previously registered TOTP authenticator.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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 logoff or notthrow err; "is_delayed_logoff": context.agenttrace.isDelayedLogoff(pSessionId), trace("Verify TOTP failed", err); if // User ID (err instanceof Java.type('dk.itp.security.passticket.PTException')) { "user_id": context.agent.getUser(pSessionId), var response = { // Customer ID "customer_id"code": isAuthenticated ? context.agent.getCustomerID(pSessionId) : null, // Agreement IDerr.getErrorCode(), "agreement_idmessage": isAuthenticated ? context.agent.getAgreementID(pSessionId) : null,err.getErrorText() }; // Is internal user "internal": isAuthenticated ?if context.agent.isInternalUser(pSessionId) : null,(!response.message) // Is logged on response.message = "Unknown error"; "logged_on": isAuthenticated, // Users real name context.respond(400, 'Verify TOTP Failed', "user_name": context.agent.getUserName(pSessionId),'application/json', JSON.stringify(response)); // IP address that created the session} else { 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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
try { var credentials // Authentication level= [pCode]; "authentication_level": context.agent.logon(context.id,50,context.agent.getAuthenticationLevelgetUser(pSessionIdcontext.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')) { var // Ticket or tokenresponse = { "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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
var body = context.getRequestBodyAsString(); try { }context.agent.executeAuthpluginCommand(context.id, 9 , 'initresetpassword', body); context.respond(200201, 'OKPassword Reset', '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; 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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
{ "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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
{ "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" } |