JWT Implementation in Harbour

JWT Implementation in Harbour

Postby Otto » Fri Jun 21, 2024 9:05 am

Hello friends,

This Harbour project demonstrates the process of creating and validating a JSON Web Token (JWT).
The workflow involves initializing the necessary variables, creating the payload, encoding the payload into a JWT, and then validating the JWT.
If the JWT is valid, the payload is displayed; otherwise, an error message is shown.

Best regards,
Otto


Explanation of the program:

Initialization:

The program initializes by setting the secret key (cSecret) and the payload (hPayload), which contains information such as the

issued time (iat),
expiration time (exp),
issuer (iss), audience (aud),
resource (resource), and
user data (data).



Payload Creation:

The payload (hPayload) is prepared with the necessary details.
JWT Creation:

The jwtEncode function is called to create a JWT (JSON Web Token) using the payload and the secret key. This function encodes the header and payload in base64 URL format, then creates a signature using HMAC SHA-256 and encodes it in base64 URL format.
JWT Display:

The generated JWT is displayed and logged.
JWT Validation:

The jwtDecode function is called to validate the JWT using the secret key. This function decodes the JWT, verifies the signature, and if valid, converts the payload from JSON to a hash table.
Check Validity:

If the JWT is valid, the payload is displayed and logged. If not, an "Invalid JWT" message is logged.
End:

The process ends after displaying or logging the payload or the error message.


Code: Select all  Expand view  RUN

#include "fivewin.ch"

FUNCTION Main()
    LOCAL cSecret
    LOCAL hPayload
    LOCAL cJwt
    LOCAL hDecoded

    cSecret := "your_secret_key"
    hPayload := { 'iat' => UnixTime(), 'exp' => UnixTime() + 3600, ;
                  'iss' => 'your_domain.com', 'aud' => 'your_application', ;
                  'resource' => 'php1', 'data' => { 'username' => 'test' } }
     
     xbrowse(hPayload, "hPayload" )
    cJwt := jwtEncode(hPayload, cSecret)
   
    ? "JWT Token:", cJwt

    hDecoded := jwtDecode(cJwt, cSecret)
   
    IF ValType(hDecoded) == 'H'
         xbrowse( hDecoded , "hDecoded")
    ELSE
        ? "Invalid JWT"
    ENDIF
RETURN NIL

FUNCTION base64UrlEncode(cData)
    LOCAL cEncoded
    cEncoded := Base64Encode(cData)
    cEncoded := StrTran(cEncoded, "+", "-")
    cEncoded := StrTran(cEncoded, "/", "_")
    cEncoded := StrTran(cEncoded, "=", "")
RETURN cEncoded

FUNCTION base64UrlDecode(cData)
    LOCAL cDataFixed
    LOCAL nMod
    cDataFixed := StrTran(cData, "-", "+")
    cDataFixed := StrTran(cDataFixed, "_", "/")
    nMod := Len(cDataFixed) % 4
    IF nMod != 0
        cDataFixed += Replicate("=", 4 - nMod)
    ENDIF
RETURN Base64Decode(cDataFixed)

FUNCTION Base64Encode(cData)
    LOCAL cOut := ""
    LOCAL nInLen := Len(cData)
    LOCAL cIn := cData
    LOCAL nOutLen, nIndex, nPad
    LOCAL nBits, nValue

    // Base64 encoding table
    LOCAL cBase64 := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

    nOutLen := Int((nInLen + 2) / 3) * 4
    cOut := Space(nOutLen)
    nIndex := 1

    FOR nBits := 1 TO nInLen STEP 3
        nValue := Asc(SubStr(cIn, nBits, 1)) * 65536 + ;
                  IIF(nBits + 1 <= nInLen, Asc(SubStr(cIn, nBits + 1, 1)), 0) * 256 + ;
                  IIF(nBits + 2 <= nInLen, Asc(SubStr(cIn, nBits + 2, 1)), 0)

        SubStr(cOut, nIndex, 1) := SubStr(cBase64, Int(nValue / 262144) + 1, 1)
        SubStr(cOut, nIndex + 1, 1) := SubStr(cBase64, Int(Mod(nValue, 262144) / 4096) + 1, 1)
        SubStr(cOut, nIndex + 2, 1) := IIF(nBits + 1 <= nInLen, SubStr(cBase64, Int(Mod(nValue, 4096) / 64) + 1, 1), "=")
        SubStr(cOut, nIndex + 3, 1) := IIF(nBits + 2 <= nInLen, SubStr(cBase64, Mod(nValue, 64) + 1, 1), "=")

        nIndex += 4
    NEXT

RETURN cOut

FUNCTION Base64Decode(cData)
    LOCAL cOut := ""
    LOCAL nInLen := Len(cData)
    LOCAL cIn := cData
    LOCAL nOutLen, nIndex, nPad
    LOCAL nValue, nBits

    // Base64 decoding table
    LOCAL cBase64 := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
    LOCAL aDecode := Array(256, 0)
    LOCAL i

    FOR i := 1 TO 64
        aDecode[Asc(SubStr(cBase64, i, 1))] := i - 1
    NEXT

    nOutLen := Int((nInLen + 3) / 4) * 3
    cOut := Space(nOutLen)
    nIndex := 1

    FOR nBits := 1 TO nInLen STEP 4
        nValue := aDecode[Asc(SubStr(cIn, nBits, 1))] * 262144 + ;
                  aDecode[Asc(SubStr(cIn, nBits + 1, 1))] * 4096 + ;
                  IIF(SubStr(cIn, nBits + 2, 1) != "=", aDecode[Asc(SubStr(cIn, nBits + 2, 1))] * 64, 0) + ;
                  IIF(SubStr(cIn, nBits + 3, 1) != "=", aDecode[Asc(SubStr(cIn, nBits + 3, 1))], 0)

        SubStr(cOut, nIndex, 1) := Chr(Int(nValue / 65536))
        IF SubStr(cIn, nBits + 2, 1) != "="
            SubStr(cOut, nIndex + 1, 1) := Chr(Int(Mod(nValue, 65536) / 256))
        ENDIF
        IF SubStr(cIn, nBits + 3, 1) != "="
            SubStr(cOut, nIndex + 2, 1) := Chr(Mod(nValue, 256))
        ENDIF

        nIndex += 3
    NEXT

RETURN RTrim(cOut)

FUNCTION hb_DateTime()
    RETURN Seconds()

FUNCTION UnixTime()
    LOCAL dEpoch := Ctod("1970-01-01")
    LOCAL dToday := Date()
    LOCAL nSeconds

    // Berechnung der Anzahl der Sekunden seit Mitternacht
    LOCAL cTime := Time()
    LOCAL nHours := Val(Left(cTime, 2))
    LOCAL nMinutes := Val(SubStr(cTime, 4, 2))
    LOCAL nSecondsPart := Val(Right(cTime, 2))

    nSeconds := (dToday - dEpoch) * 86400 + nHours * 3600 + nMinutes * 60 + nSecondsPart
RETURN nSeconds

FUNCTION jwtEncode(hPayload, cSecret)
    LOCAL hHeader
    LOCAL cHeader, cPayload
    LOCAL cBase64UrlHeader, cBase64UrlPayload
    LOCAL cSignature, cBase64UrlSignature

    hHeader := { 'typ' => 'JWT', 'alg' => 'HS256' }
    cHeader := hb_JsonEncode(hHeader)
    cPayload := hb_JsonEncode(hPayload)
    cBase64UrlHeader := base64UrlEncode(cHeader)
    cBase64UrlPayload := base64UrlEncode(cPayload)
    cSignature := hb_HMAC_SHA256(cBase64UrlHeader + "." + cBase64UrlPayload, cSecret)
    cBase64UrlSignature := base64UrlEncode(cSignature)

RETURN cBase64UrlHeader + "." + cBase64UrlPayload + "." + cBase64UrlSignature

FUNCTION jwtDecode(cJwt, cSecret)
    LOCAL aParts
    LOCAL cHeader, cPayload, cSignature
    LOCAL cBase64UrlHeader, cBase64UrlPayload, cBase64UrlSignature
    LOCAL cValidSignature
    LOCAL hDecoded

    aParts := hb_ATokens(cJwt, ".")
    IF Len(aParts) != 3
        RETURN NIL
    ENDIF

    cHeader := aParts[1]
    cPayload := aParts[2]
    cSignature := aParts[3]

    cBase64UrlHeader := base64UrlEncode(base64UrlDecode(cHeader))
    cBase64UrlPayload := base64UrlEncode(base64UrlDecode(cPayload))
    cBase64UrlSignature := base64UrlEncode(base64UrlDecode(cSignature))
    cValidSignature := base64UrlEncode(hb_HMAC_SHA256(cBase64UrlHeader + "." + cBase64UrlPayload, cSecret))

    IF cBase64UrlSignature == cValidSignature
        hDecoded := jsonToHash(base64UrlDecode(cPayload))
        RETURN hDecoded
    ELSE
        RETURN NIL
    ENDIF
RETURN NIL

FUNCTION jsonToHash(cJson)
    LOCAL hResult := {=>}  // Initialisierung als leeres Hash-Array
    LOCAL cKey, cValue, nPos, nEnd, cSubJson
    LOCAL cJsonTrimmed := Trim(cJson)
    LOCAL nLen := Len(cJsonTrimmed)
    LOCAL cChar
    LOCAL lIsArray := .F.

    IF Left(cJsonTrimmed, 1) == "[" .AND. Right(cJsonTrimmed, 1) == "]"
        cJsonTrimmed := SubStr(cJsonTrimmed, 2, nLen - 2)
        lIsArray := .T.
    ELSEIF Left(cJsonTrimmed, 1) == "{" .AND. Right(cJsonTrimmed, 1) == "}"
        cJsonTrimmed := SubStr(cJsonTrimmed, 2, nLen - 2)
    ELSE
        RETURN NIL
    ENDIF

    nPos := 1
    WHILE nPos <= Len(cJsonTrimmed)
        cChar := SubStr(cJsonTrimmed, nPos, 1)
        IF cChar == '"'
            nEnd := At('"', cJsonTrimmed, nPos + 1)
            cKey := SubStr(cJsonTrimmed, nPos + 1, nEnd - nPos - 1)
            nPos := At(":", cJsonTrimmed, nEnd) + 1
            nEnd := At(",", cJsonTrimmed, nPos)
            IF nEnd == 0
                nEnd := Len(cJsonTrimmed) + 1
            ENDIF
            cValue := Trim(SubStr(cJsonTrimmed, nPos, nEnd - nPos))
            IF Left(cValue, 1) == "{" .OR. Left(cValue, 1) == "["
                cSubJson := cValue
                cValue := jsonToHash(cSubJson)
            ELSE
                IF ValType(cValue) == "C" .AND. Left(cValue, 1) == '"' .AND. Right(cValue, 1) == '"'
                    cValue := SubStr(cValue, 2, Len(cValue) - 2)
                ELSE
                    cValue := Val(cValue)
                ENDIF
            ENDIF
            IF lIsArray
                AAdd(hResult, cValue)
            ELSE
                hResult[cKey] := cValue
            ENDIF
            nPos := nEnd + 1
        ELSE
            nPos++
        ENDIF
    ENDDO
RETURN hResult

 



Image
********************************************************************
mod harbour - Vamos a la conquista de la Web
modharbour.org
https://www.facebook.com/groups/modharbour.club
********************************************************************
User avatar
Otto
 
Posts: 6346
Joined: Fri Oct 07, 2005 7:07 pm

Return to FiveWin para Harbour/xHarbour

Who is online

Users browsing this forum: No registered users and 31 guests