Scott Fell´s token idea

fskilnik used Ask the Experts™
Hi there,

The question is related to this one:

I would like to implement the "token idea", but I am not sure I understood what should I do.

(Please explain in a step-by-step basis. Feel free to mention "this must be stored in the database", or called from there, etc, because I know how to manage an SQL server database and I AM able to create some tasks like functions or store procedures if necessary.)

Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Scott FellDeveloper & EE Moderator
Fellow 2018
Most Valuable Expert 2013

Here is the back end coding.  Enough to get you started. I didn't code the database connections, just comments.  Make sure to use parameterized queries where needed.  I also didn't include a function to make sure usernames are unique and that is something you want to make sure is set up in your db and coding.

Passwords and cookies are stored as a salted sha256 hash.  A hash is supposed to be one way meaning it can't easily be decrypted.

Each log in, a cookie is set with a token and that same token gets stored in the user table along with when you want it to expire.

Each page load, request the token from the cookie, then look it up in the user table, if found, make sure it is not expired.  If good, then return the username and user level that can be used throughout the page.

The code below is only for demonstration, not tested and should not be used as is for your log ins.  

<!-- assume a recordset of the user is recordset named rs -->
<form method="post" action="update_user_pass.asp">
	<input type="hidden" name="user_id" value="<%=rs("user_id")%>">
	<input name="pass">
	<button type="submit">Submit</button>

Open in new window

' run a query against the user table to find username'
' note we didn't pass the username in the form, just the user id
' sql = "SELECT username FROM tUsers WHERE user_id="&request.form("user_id")

Username = rs("username")
password = request.form("pass")
passHash = passwordHash(Username,password)
' sql = "UPDATE tUsers SET password = '"&passHash&"' WHERE user_id = "&request.form("user_id")

Open in new window

<form method="post" action="login.asp">
	<input name="user">
	<input name="pass">
	<button type="submit">Submit</button>

Open in new window

' ---- set up your db connection and create recorset ----

'* add your database connection or include file
'* Create recordset with sql = "SELECT password FROM tUsers WHERE username = ?" 
'* Your parameter should be the request.form("username")
'* assume recordset name is rsLogin

good_login = 0

' Is user found
IF NOT rsLogin.eof or not rsLogin.bof then
	' user is found, check password, assume hashed password
	if passwordHash(rsLogin("user"),request.form("pass"))=rsLogin("password") then
		' we are logged in.  Set a token and update user table with our function
		good_login = 1
	end if
end if

' optionally insert a record to a log in transaction table with the user_id, timestamp, ip and any other info'

if good_login = 1 then
	response.redirect "success_page.asp"
	response.redirect  "index.asp"
end if


Open in new window

' to be placed at the top on every page you want secured'
logged_in_status = "0"
strAuth = lookupToken ' find token using function'
if strAuth <> "0" then ' has data
	arrAuth=split(strAuth,"|") ' create an array from our pipe delimited return'
	logged_in_status 	= arrAuth(0)
	logged_in_user 		= arrAuth(1)
	logged_in_level 	= arrAuth(2) ' Level_1, level_2, Level_3, Admin, Author, User'
end if
if cstr(logged_in_status)="0" then
	response.redirect "/index.asp" ' not logged in, send away'
end if
' if you need to check user level for the page
if logged_in_level <> "Admin" then
	' send to another page'
end if
' note: you can add the security level to the entire page or portions of the page.


Open in new window

' **** include on every page ***** '
' <!-- #include virtual ="/includes/config.asp" -->  '

' ---- config set up ---- 
secret_key 				= "aBc123Xyz456LMNop" ' use a long random string '
login_session_minutes	= 60 ' total time per session in minutes

' ---- Set a token to cookie and db ----'
function setToken(user)
	'set token in cookie and log in table for 1 hour
	token_expires = dateAdd("n",login_session_minutes,now)
	token = sha256(now&user&secret_key)

	' assume saving 2 fields to the user table, token and expires'
	' sql = "UPDATE tUsers SET token = '"&token&"', token_expires = '"&token_expires&"'
	' update your user table

end function

' ---- look up cookie----'
function lookupToken()
	lookupToken="0" ' return bad '
	foundToken = Request.Cookies("domaintoken")
	if foundToken<>"" then ' if token cookie found then look up db'
		' look up the user table based on token
		' sql = "SELECT username, security_level  FROM tUsers WHERE token = '"&foundToken&"' AND token_expires > "&now
		' create recordset called rsFoundToken
		IF not rsFoundToken.bof or not rsFoundToken.eof then
			' pipe delimited return  username|admin'
		end if
	end if
end function

' ---- Hash a password ----'
function passwordHash(user,pass)

end function

' ---- SHA 256 FUNCTION ----
' See the VB6 project that accompanies this sample for full code comments on how
' it works.
' ASP VBScript code for generating a SHA256 'digest' or 'signature' of a string. The
' MD5 algorithm is one of the industry standard methods for generating digital
' signatures. It is generically known as a digest, digital signature, one-way
' encryption, hash or checksum algorithm. A common use for SHA256 is for password
' encryption as it is one-way in nature, that does not mean that your passwords
' are not free from a dictionary attack.
' If you are using the routine for passwords, you can make it a little more secure
' by concatenating some known random characters to the password before you generate
' the signature and on subsequent tests, so even if a hacker knows you are using
' SHA-256 for your passwords, the random characters will make it harder to dictionary
' attack.
' NOTE: Due to the way in which the string is processed the routine assumes a
' single byte character set. VB passes unicode (2-byte) character strings, the
' ConvertToWordArray function uses on the first byte for each character. This
' has been done this way for ease of use, to make the routine truely portable
' you could accept a byte array instead, it would then be up to the calling
' routine to make sure that the byte array is generated from their string in
' a manner consistent with the string type.
' This is 'free' software with the following restrictions:
' You may not redistribute this code as a 'sample' or 'demo'. However, you are free
' to use the source code in your own code, but you may not claim that you created
' the sample code. It is expressly forbidden to sell or profit from this source code
' other than by the knowledge gained or the enhanced value added by your own code.
' Use of this software is also done so at your own risk. The code is supplied as
' is without warranty or guarantee of any kind.
' Should you wish to commission some derivative work based on this code provided
' here, or any consultancy work, please do not hesitate to contact us.
' Web Site:
' E-mail:

Private m_lOnBits(30)
Private m_l2Power(30)
Private K(63)

Private Const BITS_TO_A_BYTE = 8
Private Const BYTES_TO_A_WORD = 4
Private Const BITS_TO_A_WORD = 32

m_lOnBits(0) = CLng(1)
m_lOnBits(1) = CLng(3)
m_lOnBits(2) = CLng(7)
m_lOnBits(3) = CLng(15)
m_lOnBits(4) = CLng(31)
m_lOnBits(5) = CLng(63)
m_lOnBits(6) = CLng(127)
m_lOnBits(7) = CLng(255)
m_lOnBits(8) = CLng(511)
m_lOnBits(9) = CLng(1023)
m_lOnBits(10) = CLng(2047)
m_lOnBits(11) = CLng(4095)
m_lOnBits(12) = CLng(8191)
m_lOnBits(13) = CLng(16383)
m_lOnBits(14) = CLng(32767)
m_lOnBits(15) = CLng(65535)
m_lOnBits(16) = CLng(131071)
m_lOnBits(17) = CLng(262143)
m_lOnBits(18) = CLng(524287)
m_lOnBits(19) = CLng(1048575)
m_lOnBits(20) = CLng(2097151)
m_lOnBits(21) = CLng(4194303)
m_lOnBits(22) = CLng(8388607)
m_lOnBits(23) = CLng(16777215)
m_lOnBits(24) = CLng(33554431)
m_lOnBits(25) = CLng(67108863)
m_lOnBits(26) = CLng(134217727)
m_lOnBits(27) = CLng(268435455)
m_lOnBits(28) = CLng(536870911)
m_lOnBits(29) = CLng(1073741823)
m_lOnBits(30) = CLng(2147483647)

m_l2Power(0) = CLng(1)
m_l2Power(1) = CLng(2)
m_l2Power(2) = CLng(4)
m_l2Power(3) = CLng(8)
m_l2Power(4) = CLng(16)
m_l2Power(5) = CLng(32)
m_l2Power(6) = CLng(64)
m_l2Power(7) = CLng(128)
m_l2Power(8) = CLng(256)
m_l2Power(9) = CLng(512)
m_l2Power(10) = CLng(1024)
m_l2Power(11) = CLng(2048)
m_l2Power(12) = CLng(4096)
m_l2Power(13) = CLng(8192)
m_l2Power(14) = CLng(16384)
m_l2Power(15) = CLng(32768)
m_l2Power(16) = CLng(65536)
m_l2Power(17) = CLng(131072)
m_l2Power(18) = CLng(262144)
m_l2Power(19) = CLng(524288)
m_l2Power(20) = CLng(1048576)
m_l2Power(21) = CLng(2097152)
m_l2Power(22) = CLng(4194304)
m_l2Power(23) = CLng(8388608)
m_l2Power(24) = CLng(16777216)
m_l2Power(25) = CLng(33554432)
m_l2Power(26) = CLng(67108864)
m_l2Power(27) = CLng(134217728)
m_l2Power(28) = CLng(268435456)
m_l2Power(29) = CLng(536870912)
m_l2Power(30) = CLng(1073741824)

K(0) = &H428A2F98
K(1) = &H71374491
K(2) = &HB5C0FBCF
K(3) = &HE9B5DBA5
K(4) = &H3956C25B
K(5) = &H59F111F1
K(6) = &H923F82A4
K(7) = &HAB1C5ED5
K(8) = &HD807AA98
K(9) = &H12835B01
K(10) = &H243185BE
K(11) = &H550C7DC3
K(12) = &H72BE5D74
K(13) = &H80DEB1FE
K(14) = &H9BDC06A7
K(15) = &HC19BF174
K(16) = &HE49B69C1
K(17) = &HEFBE4786
K(18) = &HFC19DC6
K(19) = &H240CA1CC
K(20) = &H2DE92C6F
K(21) = &H4A7484AA
K(22) = &H5CB0A9DC
K(23) = &H76F988DA
K(24) = &H983E5152
K(25) = &HA831C66D
K(26) = &HB00327C8
K(27) = &HBF597FC7
K(28) = &HC6E00BF3
K(29) = &HD5A79147
K(30) = &H6CA6351
K(31) = &H14292967
K(32) = &H27B70A85
K(33) = &H2E1B2138
K(34) = &H4D2C6DFC
K(35) = &H53380D13
K(36) = &H650A7354
K(37) = &H766A0ABB
K(38) = &H81C2C92E
K(39) = &H92722C85
K(40) = &HA2BFE8A1
K(41) = &HA81A664B
K(42) = &HC24B8B70
K(43) = &HC76C51A3
K(44) = &HD192E819
K(45) = &HD6990624
K(46) = &HF40E3585
K(47) = &H106AA070
K(48) = &H19A4C116
K(49) = &H1E376C08
K(50) = &H2748774C
K(51) = &H34B0BCB5
K(52) = &H391C0CB3
K(53) = &H4ED8AA4A
K(54) = &H5B9CCA4F
K(55) = &H682E6FF3
K(56) = &H748F82EE
K(57) = &H78A5636F
K(58) = &H84C87814
K(59) = &H8CC70208
K(60) = &H90BEFFFA
K(61) = &HA4506CEB
K(62) = &HBEF9A3F7
K(63) = &HC67178F2

Private Function LShift(lValue, iShiftBits)
    If iShiftBits = 0 Then
        LShift = lValue
        Exit Function
    ElseIf iShiftBits = 31 Then
        If lValue And 1 Then
            LShift = &H80000000
            LShift = 0
        End If
        Exit Function
    ElseIf iShiftBits < 0 Or iShiftBits > 31 Then
        Err.Raise 6
    End If

    If (lValue And m_l2Power(31 - iShiftBits)) Then
        LShift = ((lValue And m_lOnBits(31 - (iShiftBits + 1))) * m_l2Power(iShiftBits)) Or &H80000000
        LShift = ((lValue And m_lOnBits(31 - iShiftBits)) * m_l2Power(iShiftBits))
    End If
End Function

Private Function RShift(lValue, iShiftBits)
    If iShiftBits = 0 Then
        RShift = lValue
        Exit Function
    ElseIf iShiftBits = 31 Then
        If lValue And &H80000000 Then
            RShift = 1
            RShift = 0
        End If
        Exit Function
    ElseIf iShiftBits < 0 Or iShiftBits > 31 Then
        Err.Raise 6
    End If

    RShift = (lValue And &H7FFFFFFE) \ m_l2Power(iShiftBits)

    If (lValue And &H80000000) Then
        RShift = (RShift Or (&H40000000 \ m_l2Power(iShiftBits - 1)))
    End If
End Function

Private Function AddUnsigned(lX, lY)
    Dim lX4
    Dim lY4
    Dim lX8
    Dim lY8
    Dim lResult

    lX8 = lX And &H80000000
    lY8 = lY And &H80000000
    lX4 = lX And &H40000000
    lY4 = lY And &H40000000

    lResult = (lX And &H3FFFFFFF) + (lY And &H3FFFFFFF)

    If lX4 And lY4 Then
        lResult = lResult Xor &H80000000 Xor lX8 Xor lY8
    ElseIf lX4 Or lY4 Then
        If lResult And &H40000000 Then
            lResult = lResult Xor &HC0000000 Xor lX8 Xor lY8
            lResult = lResult Xor &H40000000 Xor lX8 Xor lY8
        End If
        lResult = lResult Xor lX8 Xor lY8
    End If

    AddUnsigned = lResult
End Function

Private Function Ch(x, y, z)
    Ch = ((x And y) Xor ((Not x) And z))
End Function

Private Function Maj(x, y, z)
    Maj = ((x And y) Xor (x And z) Xor (y And z))
End Function

Private Function S(x, n)
    S = (RShift(x, (n And m_lOnBits(4))) Or LShift(x, (32 - (n And m_lOnBits(4)))))
End Function

Private Function R(x, n)
    R = RShift(x, CInt(n And m_lOnBits(4)))
End Function

Private Function Sigma0(x)
    Sigma0 = (S(x, 2) Xor S(x, 13) Xor S(x, 22))
End Function

Private Function Sigma1(x)
    Sigma1 = (S(x, 6) Xor S(x, 11) Xor S(x, 25))
End Function

Private Function Gamma0(x)
    Gamma0 = (S(x, 7) Xor S(x, 18) Xor R(x, 3))
End Function

Private Function Gamma1(x)
    Gamma1 = (S(x, 17) Xor S(x, 19) Xor R(x, 10))
End Function

Private Function ConvertToWordArray(sMessage)
    Dim lMessageLength
    Dim lNumberOfWords
    Dim lWordArray()
    Dim lBytePosition
    Dim lByteCount
    Dim lWordCount
    Dim lByte

    Const MODULUS_BITS = 512
    Const CONGRUENT_BITS = 448

    lMessageLength = Len(sMessage)

    ReDim lWordArray(lNumberOfWords - 1)

    lBytePosition = 0
    lByteCount = 0
    Do Until lByteCount >= lMessageLength
        lWordCount = lByteCount \ BYTES_TO_A_WORD

        lBytePosition = (3 - (lByteCount Mod BYTES_TO_A_WORD)) * BITS_TO_A_BYTE

        lByte = AscB(Mid(sMessage, lByteCount + 1, 1))

        lWordArray(lWordCount) = lWordArray(lWordCount) Or LShift(lByte, lBytePosition)
        lByteCount = lByteCount + 1

    lWordCount = lByteCount \ BYTES_TO_A_WORD
    lBytePosition = (3 - (lByteCount Mod BYTES_TO_A_WORD)) * BITS_TO_A_BYTE

    lWordArray(lWordCount) = lWordArray(lWordCount) Or LShift(&H80, lBytePosition)

    lWordArray(lNumberOfWords - 1) = LShift(lMessageLength, 3)
    lWordArray(lNumberOfWords - 2) = RShift(lMessageLength, 29)

    ConvertToWordArray = lWordArray
End Function

Public Function SHA256(sMessage)
    Dim HASH(7)
    Dim M
    Dim W(63)
    Dim a
    Dim b
    Dim c
    Dim d
    Dim e
    Dim f
    Dim g
    Dim h
    Dim i
    Dim j
    Dim T1
    Dim T2

    HASH(0) = &H6A09E667
    HASH(1) = &HBB67AE85
    HASH(2) = &H3C6EF372
    HASH(3) = &HA54FF53A
    HASH(4) = &H510E527F
    HASH(5) = &H9B05688C
    HASH(6) = &H1F83D9AB
    HASH(7) = &H5BE0CD19

    M = ConvertToWordArray(sMessage)

    For i = 0 To UBound(M) Step 16
        a = HASH(0)
        b = HASH(1)
        c = HASH(2)
        d = HASH(3)
        e = HASH(4)
        f = HASH(5)
        g = HASH(6)
        h = HASH(7)

        For j = 0 To 63
            If j < 16 Then
                W(j) = M(j + i)
                W(j) = AddUnsigned(AddUnsigned(AddUnsigned(Gamma1(W(j - 2)), W(j - 7)), Gamma0(W(j - 15))), W(j - 16))
            End If

            T1 = AddUnsigned(AddUnsigned(AddUnsigned(AddUnsigned(h, Sigma1(e)), Ch(e, f, g)), K(j)), W(j))
            T2 = AddUnsigned(Sigma0(a), Maj(a, b, c))

            h = g
            g = f
            f = e
            e = AddUnsigned(d, T1)
            d = c
            c = b
            b = a
            a = AddUnsigned(T1, T2)

        HASH(0) = AddUnsigned(a, HASH(0))
        HASH(1) = AddUnsigned(b, HASH(1))
        HASH(2) = AddUnsigned(c, HASH(2))
        HASH(3) = AddUnsigned(d, HASH(3))
        HASH(4) = AddUnsigned(e, HASH(4))
        HASH(5) = AddUnsigned(f, HASH(5))
        HASH(6) = AddUnsigned(g, HASH(6))
        HASH(7) = AddUnsigned(h, HASH(7))

    SHA256 = LCase(Right("00000000" & Hex(HASH(0)), 8) & Right("00000000" & Hex(HASH(1)), 8) & Right("00000000" & Hex(HASH(2)), 8) & Right("00000000" & Hex(HASH(3)), 8) & Right("00000000" & Hex(HASH(4)), 8) & Right("00000000" & Hex(HASH(5)), 8) & Right("00000000" & Hex(HASH(6)), 8) & Right("00000000" & Hex(HASH(7)), 8))
End Function

Open in new window


Hi there, Scott Fell !!

Uau... this is simply MARVELLOUS.  

I will start studying and implementing your ideas/solutions tomorrow (Monday), and I will be back, every two days at most, to tell you about my progress and, eventually, my doubts.

I am REALLY REALLY grateful.

Thanks A LOT for all the time and patience you had to explain/create this all !!!!



Hi there, Scott!

I have read your "scripts" carefully and I guess I understood the whole idea.

My first steps:

(1) I have already created the login page before, and it works ok. If the users logs in successfully, two things were already implemented:

first:  a session "UserLogin" was created, that you call username  (this is an EMAIL of the user, a nvarchar(90) field);
second: a session "UserID" is created, that you call user_id  (this is an integer field) ;

Now a third thing should happen:

three:  the function setToken(Session("UserID"))  should run, considering that I included the "token_task.asp" file (that you called "config.asp") at the begining of the page:

<!--#include virtual="token_task.asp" -->

Important: I have created the two additional fields in the users table: token  AND token_expires, the first one a "nvarchar(50)" field and the second one a datetime field.

Some error occur and, after many tests, I am SURE the problem is in this line:

token = sha256(now&user&secret_key)

on the code given below. Why? Because if I put  "token = 12345" as a substitute, it works as expected.

Of course I have copy-pasted the whole function definitions present at your config.asp page ...

function setToken(user)
      'set token in cookie and log in table for 1 hour
      token_expires = dateAdd("n",login_session_minutes,now)
      token = sha256(now&user&secret_key)
Set objConnection = Server.CreateObject("ADODB.Connection")  MM_myConnection_server_STRING
SQLsproc =  "UPDATE dbo.properTblUsers SET token = '" & token & "' WHERE properUserID = " & user

objConnection.CommandTimeout = 0

objconnection.execute SQLsproc

end function

You may note I did not put the last part in the update procedure, because I believe the "cdate(expires)" should be "cdate(token_expires)"  anyway, correct?  I also took out the "cookies" lines because I don´t know if I should define something related to these cookies previously, I mean, in other pages. I did not.

Please help.

Thanks a lot!


P.S.: I still did not put the lookupToken() function to work, by the way.
Exploring SQL Server 2016: Fundamentals

Learn the fundamentals of Microsoft SQL Server, a relational database management system that stores and retrieves data when requested by other software applications.

Scott FellDeveloper & EE Moderator
Fellow 2018
Most Valuable Expert 2013

There are a couple of reason for creating the log in this way.  For one can potentially be more secure because the token will change at each log in.  Also, it eliminates the possibility that the session expired unexpectedly such as the worker process crashing and resetting the session or somebody staring at the screen for 10 minutes only to hit the submit button and get the time out error from being idle.

 If the line token = sha256(now&user&secret_key) is causing an error but you say you can use "token = 12345", the first thing to check is that the sha256 function is working.

Try something like response.write sha256("abc") and see what happens.   If that is not working, make sure  you have that in your scripts.asp.    If it does work, the next thing to check is that you are passing data.   Try using response.write user then response.write secret_key just to make sure you have those variables loaded with data or not misspelled.   It will help if you use option explicit  Also, make sure everything is converted to a string.  IF the variable user could be a number, then make sure it is a string.

token = sha256(cstr(now)&cstr(user)&secret_key)

Once that is working, there should be a cookie that has the token string and a matching string in the user table.  On each page load, I look up the cookie and match it with the user table field that contains the data.  That will give you all the user fields including the expired, username, full name, user level etc that you can use later on in the page if needed.

Then you can do a way with storing this in a session.


Hi there, Scott Fell!

Excellent suggestions and testing-hints. After suffering a lot (as usual), I realized there were MANY problems:

1. The secret_key was defined in the wrong page
2. The "updating procedure" was not working inside the function, therefore I made the function redirect the "code" to another page, created only for updating the token, then it goes back to the previous page.
3. The conversion to strings inside sha256 were not necessary, but I thought that was the problem until I realized the ones above... typical... computing = maximizing pain, isn´t it?    :)

Now the good news: at this moment, after all corrections, when the user logs in, the token is saved as expected, and the "now" component really makes the token be different each time the (same) user logs in... great!

Tomorrow (Thursday) I will continue from this point on and I will let you know my progress or my next doubts!

Thanks A LOT, really. I am really proud to be able to implement your suggestions... and you are great to help me on this whole stuff!!



Hello, Scott!

I am sorry for not coming back here for the last few days, but last week I was really busy and could not implement any other part of the phases of the solution you have provided.

Tomorrow (Tuesday) I will have some free hours and I will restart from where I´ve left.

Thanks a lot for your understanding and, again, I am sorry about my delay for this message.

Regards and have a great day/week!


Hi there Scott Fell,

Now I made a lot of progress and, what is great, I UNDERSTOOD how cookies are connected with the whole thing!   :)

Well, let´s go to my question, please:

In a certain page, let us call it  "Page01_test" , I call another page ("config.asp" as you called) to run the "lookupToken" function.

In the "Page01_test" page, I KNOW the "foundToken" string was properly extracted from the database, but when the "lookupToken" function runs, something wrong occurs...

> when I use the test line:

rsFoundToken.Source = "SELECT UserLogin, security_level FROM dbo.TblUsers WHERE userID = " & 5

it works as expected, I mean, I am able to create the lookupToken string successfully...

but when I try the almost-real test line:

rsFoundToken.Source = "SELECT UserLogin, security_level FROM dbo.TblUsers WHERE token = '" & foundToken & "' AND userID = " & 5

I get an error... I mean, the rsFoundToken.Source  is NOT able to extract the line from the database in one case, but it can in the other. I am sure the token = ....  is with something missing, perhaps the long string is making some trouble when it is looked into the database??

I have tried some changes like putting cstr(foundToken) , for instance, but it does not fix the problem, therefore I guess the problem is something out of my limited knowledge... the WHOLE (testing) function, to help you help me, follows:

Thanks a lot, as usual!!


function lookupToken()
  lookupToken = "1|abc|User" ' return bad '      <----------  for testing purposes

  foundToken = Request.Cookies("domain_token")                <--------- I´ve called it like that, with "_" included...
  If foundToken <> "" then ' if token cookie found then look up db'       <---- from test I know  foundToken is not empty...
  ' look up the user table based on token
  Dim  rsFoundToken
      Set  rsFoundToken = Server.CreateObject("ADODB.Recordset")
             rsFoundToken.ActiveConnection = MM_myConnection_server_STRING
             rsFoundToken.Source = "SELECT UserLogin, security_level FROM dbo.TblUsers WHERE token = '" & foundToken & "' AND userID = " & 5
             rsFoundToken.CursorType = 0
             rsFoundToken.CursorLocation = 2
             rsFoundToken.LockType = 1
       IF not rsFoundToken.bof or not rsFoundToken.eof then
       ' pipe delimited return  username|admin'
       lookupToken = "1|def|User"
       End If
   End If
End function

As you certainly understand, my test is to see if (say) abc is changed to def ...
Scott FellDeveloper & EE Moderator
Fellow 2018
Most Valuable Expert 2013

>rsFoundToken.Source = "SELECT UserLogin, security_level FROM dbo.TblUsers WHERE token = '" & foundToken & "' AND userID = " & 5

At the top of the page, do a response.write foundToken and what do you get?  Does it match what is currently in the database?  If it helps, sometimes I use response.write "|"&foundToken&"|" to help detect if there is a space at either end.  You can eliminate that by using trim(" something ") which will yield, "something".


Hi, Scott!

Thanks for the nice testing-hints.  "Unfortunately" I got the proper "message" (below) in the same page that calls the "config.asp" page where the "lookUpToken" function is placed.

That means that the foundToken IS correctly being found... I will try another more tests and let you know in say 1.5h from now...

Regards and many thanks,


lookupToken = 1|def|User
logged_in_user = def
foundToken = |2a3f56c9814efde8f564f5004dec9d9634cfc73f825a454834af05b59915f747|



Hi there, Scott!

Yep... found the mistake... from your wonderful testing-idea :

The foundToken shown above was LONGER than the one saved in the table field... reason:  the "token" was defined as nvarchar(50), now I´ve changed it to nvarchar(200) and it worked as expected (saving the complete token value)!!

I will continue implementing your great "token idea" and I will be back tomorrow (Thursday) or on Friday, to tell you about my progress and my new doubts.

Thanks A LOT for all this, of course.  Your expertise and time devoted to help me is really very very appreciated.

Kind Regards,
Scott FellDeveloper & EE Moderator
Fellow 2018
Most Valuable Expert 2013

Sounds good.  I will not be online thursday.


Thanks for the reply, Scott!

No problem at all, of course.

See you on Friday, then!  Perhaps with the solution fully implemented...    ;)

Scott FellDeveloper & EE Moderator
Fellow 2018
Most Valuable Expert 2013

I just created an article based on this that has  full working sample code.  It may take a few days to be public but watch for it
Developer & EE Moderator
Fellow 2018
Most Valuable Expert 2013


Hi there, Scott!

Sorry again for the delay, but last week I moved from one place to another (I mean, my house) and my "free time" (not teaching time nor creating materials/homwork for my students) is being used to deal with plumbers, painters, etc... that means I´ve seen your last 2 posts above only now.

I will restart working on the "token solution" tomorrow and the first thing I will do is to read the links you´ve created and kindly provided!!

Thanks a lot, as usual!!

Kind Regards,


I´m back, Scott!  Again with delay, sorry for that.

To be honest, in the last few days I was NOT able to work on your solution (unfortunately) - for the reasons above and also because at this week I have a lot of online math material to create/organize - but now, after reading a good part of your marvellous article, I understand the "whole" plan/process" even clearer, and I guess it´s more-than-fair to consider my question absolutely solved. You were/are simply great, no doubt!!

If I find any trouble in implementing the small bits I´ve not done yet, I will open another EE post and, of course, let you know posting the link here.

On the other hand, I PROMISE to enter another comment here when your solution is fully implemented in my site.   :)

Thanks A LOT and may I have the luck to count on your expertise in other programming issues!

All the best and Kind Regards,
Fabio Skilnik (fskilnik)

P.S.: I am honoured to have created two EE posts that, in a sense, "motivated" you to (re)think about the whole matter and write a beautiful article!


Hi, Scott!

As promised, I came back to tell you that your solution was put on my site two days ago.

The only 2 differences between your full solution and what I´ve done are these:

1. The token fields were created in the "Users" table, not in the (certainly better) "login transaction table" as you suggest. In the future I intend to make this modification, also for the sake of (as you say): "... you can run a report of who logged in with the time stamp and IP.  You can also view the current list of logged in users and if you like, remove their token and thus logging them out."

2. I have not used the hash function to avoid letting the explicit password from each user to be stored in the database (among other issues). Again I believe that your suggestion IS excellent, and I do intend to change my code in the near future.

Let me ask you something: when the lookupToken() function is called, this line:

>> foundToken = Request.Cookies("domaintoken")

will work even if the user does not allow cookies in his computer?

The reason for the question is the fact that one user told me that after logged in (from the homepage) he was redirected to the login page (not the homepage) when he tried to go from the first logged in page to another one.  

If the problem is not cookie-related, could you imagine what the problem could be?

Thanks A LOT, as always!

Scott FellDeveloper & EE Moderator
Fellow 2018
Most Valuable Expert 2013

The user would have to allow cookies for this to work and that is probably the issue.


Hi, Scott!

Thank you for the quick reply. Ok, I will let him know about it!

See you in my future EE posts, I hope!    :)

Scott FellDeveloper & EE Moderator
Fellow 2018
Most Valuable Expert 2013

What you can do is on the log in page set a test cookie than try and read it.  If you can't read it you can tell cookies are turned off.  If that is the case, send a message on screen that cookies must be enabled.

if request.cookies("test")<>"ok" then
     response.write "Cookies must be enabled to use this site"
end if

Open in new window


Excellent, Scott!

I will do that, for sure.

Thanks a lot (as usual)!!


Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial