Skip to content
login.html 10.8 KiB
Newer Older
<!DOCTYPE html>
<html>
    <head>
        {% for src in js %}
            <script type="text/javascript" src="{{ src }}"></script>
        {% endfor %}
        {% for src in css %}
            <link type="text/css" rel="stylesheet" href="{{ src }}">
        {% endfor %}
        
        <script type="text/javascript" src="{{ url_for( 'send_app_files', subpath = 'webauthn.js' ) }}"></script>
        <script type="text/javascript" src="{{ url_for( 'send_app_files', subpath = 'functions.js' ) }}"></script>
        <link type="text/css" rel="stylesheet" href="{{ url_for( 'send_app_files', subpath = 'app.css' ) }}">
        
        <script type="text/javascript">
            baseurl = "{{ baseurl }}";
        </script>
    </head>
    <body>
Marco De Donno's avatar
Marco De Donno committed
        <div class="icnml_central">
            <h1 style="margin-bottom: 0px">ICNML</h1>
            <h4 style="margin-top: 0px">International Close Non-Matches Library</h4>
            
Marco De Donno's avatar
Marco De Donno committed
            <div class="ui-widget-header ui-corner-top icnml_box_top">Please enter your login information</div>
            <div id="icnml_homepage_form" class="ui-widget-content ui-corner-bottom icnml_box_content">
                <div id="icnml_box_fields" class="icnml_box_fields">
                    <div style="text-align: right;">
                        <label for="username">Username</label>
                    </div>
                    <div>
                        <input id="username" name="username" type="text" />
                    </div>
                    <div style="text-align: right;">
                        <label for="password">Password</label>
                    </div>
                    <div>
                        <input id="password" name="password" type="password" />
                    </div>
                </div>
Marco De Donno's avatar
Marco De Donno committed
                <div id="icnml_login_error" class="icnml_box_error"></div>
                <div id="icnml_login_warning" class="icnml_box_warning"></div>
                <div class="icnml_button">
                    <a class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" id="login_button" role="button" aria-disabled="false">
Marco De Donno's avatar
Marco De Donno committed
                        <span class="ui-button-text">Login</span>
                    </a>
                </div>
Marco De Donno's avatar
Marco De Donno committed
                <div class="icnml_shybox" id="password_reset">
                    <a href="{{ url_for( 'password_reset' ) }}">Password reset</a>
                </div>
                <div class="icnml_shybox" id="new_account">
                    <a href="{{ url_for( 'new_user' ) }}">Request an account</a>
Marco De Donno's avatar
Marco De Donno committed
            </div>
            <div class="icnml_shybox" style="margin-top: 10px;">
                <span id="icnml_version"></span>
            </div>
            <div class="icnml_shybox">
                <a id="icnml_tree_files" href="#"></a>
            </div>
            <div class="icnml_shybox">
                <a id="icnml_documents" href="https://esc-md-git.unil.ch/ICNML/documents" style="text-decoration: underline">See the documents related to the ICNML project</a>
        </div>
    </body>
    <script type="text/javascript">
        var login_action_password = async function()
        {
            $( "#icnml_login_error" ).html( "" );
            $( "#login_button > span" ).text( "Please wait..." );
            
            var username = $( "#username" ).val();
            var password = $( "#password" ).val();
            var password_local = $( "#password" ).val();
            
            password = await generateKey( password, "icnml_" + username, 20000 );
            password = password.substring( 0, 128 );
            password = "pbkdf2$sha512$icnml_" + username + "$20000$" + password;
            
            password_local = await generateKey( password_local, "icnml_" + username + "_localpassword", 50000 );
            password_local = password_local.substring( 0, 128 );
            
            var e = encrypt( password_local, "{{ session_security_key }}" );
            sessionStorage.setItem( "session_key", e );
            
            $.ajax( {
                url: "{{ url_for( 'do_login' ) }}",
                dataType: "json",
                method: "POST",
                data: {
                    username: username,
                    password: password
                },
                success: function( data )
                {
                    if( ! data.error )
                    {
                        if( data.logged )
                        {
                            location.href = "{{ url_for( 'home' ) }}";
                        
                        } else if( data.next_step === "totp" ) {
                            build_totp_form();
                        
                        } else if( data.next_step === "securitykey" ) {
                            build_securitykey_form();
                        
                        } else {
                            if( typeof data.message !== "undefined" )
                                var message = data.message;
                            else
                                var message = "Invalid username/password";
                            
                            $( "#icnml_login_error" ).text( message );
                        }
                        
                        $( "#login_button > span" ).text( "Login" );
                    }
                },
Marco De Donno's avatar
Marco De Donno committed
                error: function( data )
                {
                    $( "#icnml_login_error" ).text( "Network error" );
                }
            } );
        }
        
        var login_action_totp = function()
        {
            $( "#icnml_login_error" ).html( "" );
            $( "#login_button > span" ).text( "Please wait..." );
            
            $.ajax( {
                url: "{{ url_for( 'do_login' ) }}",
                dataType: "json",
                method: "POST",
                data: {
                    totp: $( "#totp" ).val()
                },
                success: function( data )
                {
                    if( ! data.error )
                    {
                        if( data.logged )
                        {
                            location.href = "{{ url_for( 'home' ) }}";
                        
                        } else if( data.next_step === "securitykey" ) {
                            build_securitykey_form();
                        
                        } else {
                            if( typeof data.message !== "undefined" )
Marco De Donno's avatar
Marco De Donno committed
                                $( "#icnml_login_error" ).text( data.message );
                            
                            if( typeof data.time !== "undefined" )
Marco De Donno's avatar
Marco De Donno committed
                                $( "#icnml_login_warning" ).text( "Current server time: " + moment.utc( data.time * 1000 ).local().format( "MMMM Do YYYY, HH:mm:ss" ) );
                },
                error: function()
                {
                     $( "#icnml_login_error" ).text( "Network error" );
                }
            } );
        }
        
        var build_totp_form = function()
        {
            $( "#icnml_login_error" ).html( "" );
            $( "#icnml_box_fields" ).html( "" );
            
            $( "#password_reset" ).remove();
            $( "#new_account" ).remove();
            
            $( "#icnml_box_fields" )
                .append(
                    $( "<div />" )
                        .text( "TOTP" )
                        .css( "text-align", "right" )
                )
                .append(
                    $( "<div />" ).append(
                        $( "<input />" )
                            .attr( "id", "totp" )
                            .attr( "name", "totp" )
                            .on( "keyup", function( event )
                            {
                                if( event.keyCode == 13 )
                                {
                                    event.preventDefault();
                                    login_action_totp();
                                }
                            } )
                    )
                );
            
            $( "#totp" ).focus();
        }
        
        var build_securitykey_form = function()
        {
            $( "#icnml_login_error" ).html( "" );
            $( "#icnml_box_fields" ).html( "" );
            
            $( "#icnml_box_fields" )
                .removeClass( "icnml_box_fields" )
                .addClass( "icnml_auto" );
            
            $( "#login_button" ).remove();
            $( "#password_reset" ).remove();
            
            $( "#icnml_homepage_form" )
                .prepend(
                    $( "<div />" )
                        .css( "margin-bottom", "10px" )
                        .text( "Logging with your security key..." )
                );
            
            login_key();
        }
        
        /* Events binding */
        $( "#username" ).on( "keyup", function( event )
        {
            if( event.keyCode == 13 )
            {
                event.preventDefault();
                login_action_password();
            }
        } );
        $( "#password" ).on( "keyup", function( event )
        {
            if( event.keyCode == 13 )
            {
                event.preventDefault();
                login_action_password();
            }
        } );
        $( "#login_button" ).on( "click", login_action_password );
Marco De Donno's avatar
Marco De Donno committed
        
        $( "#username" ).focus();
        
        $( document ).ready( function()
        {
            if( !window.crypto || !window.crypto.subtle || !window.TextEncoder || !window.TextDecoder)
            {
                $( "#icnml_login_warning" )
                    .text( "Your browser does not support client-side cryptography. Please use compatible browser (Firefox, Chrome, Opera, Safari, ...) to protect your password before sending it to the ICNML server." );
                $( "#username" ).prop( "disabled", true );
                $( "#password" ).prop( "disabled", true );
                $( "#login_button" ).remove();
        {
            location.reload();
        }, {{ session_timeout }} * 1000 );
        
        /* Get the version of ICNML */
        $.ajax( {
            url: "{{ url_for( 'version' ) }}",
            dataType: "json",
            method: "GET",
            success: function( data ){
                $( "#icnml_version" )
                    .text( "ICNML version: " + data.version );
                
                $( "#icnml_tree_files" )
                    .attr( "href", data.treeurl )
                    .attr( "target", "_blank" )
                    .css( "text-decoration", "underline" )
                    .text( "See and download the source code here" );
    </script>
</html>
Marco De Donno's avatar
Marco De Donno committed