From 8d6055752e0334b4392c4086ce48aa5d7c807501 Mon Sep 17 00:00:00 2001 From: Matteo Baccan Date: Fri, 11 Mar 2022 02:20:52 +0100 Subject: [PATCH] Securized class --- lib/jwt.hrb | Bin 3719 -> 4369 bytes out/jwttest.hrb | Bin 1913 -> 2012 bytes out/jwttest.log | 16 +++++---- src/jwt.prg | 85 +++++++++++++++++++++++++++++------------------ test/jwttest.prg | 9 +++-- 5 files changed, 68 insertions(+), 42 deletions(-) diff --git a/lib/jwt.hrb b/lib/jwt.hrb index b68ebe020f3369e0f26c21c61cc7fe1afeee098e..034953c5332b3c68c552ac776cc464cf2854e064 100644 GIT binary patch literal 4369 zcmZ`+>u=lE6+a{;%TDO{Q74O2c~RS&*U}`kW9QK(Kq-kcnaGkR(k-;AV-$;aq^go8 z%bV>zz6@B$ztC&VCy~%7_bHCwhwy&`>+lB2lfx_9P*MPRn1TF z-t#-Z^S+nw>6NSqw*lbJOBN&r+s+ltLMeAQuYD9iENU-72J~#MpqeH=>)BHFj+V3V zqngieY!)m7<@2hgq8P1cmR`!^XVhkARa3jRx>+e`#at<`fhfdQ^ICMZYH3!X;wwM^^CY7`4bQ6mdowCi@bPl z7Kafvil(I&bN(I%Q&-Ima~*XUMZ@yLoB^Mm>{h!wdECmLA6j>(JwjAq0pMpWxkRD%v6|8$_j?`u2w}ok(6Wr zi4#Du8YeYXFV_#+-FhPwJ2_4)wWam@-Jz~i zpiH1$t=VjS*THt}x3$hzx8XoLMV8WcPcqn(9}vqszSv|`EX~EHvj+pm>Y9Uov$F&3 zsi@3SAhYfa%;rv~KG^Fc-I1uwX^JU(ic2Pn&ZnV$)D~)!p+SuAO`6WYI_RMSIjZ0c zDAVNDB$9aB)e|6KN|j?^3q4HJ;GhS>L=ky)&ZwS&ln`x{cl4 zJ*=VX(3@EvistU_h&Pu49P$M??no3AcjPh$9Yr#8?|#eK8jNGx-Mc@+b|nBJTL>JX zBUGGD;V7Bnw%2Gnoh@e^UwyAJ!nYhCDVia2gpgoy!W9miMnY}B*RKzn-Oe~hXTLYX zcr}0`V+1-vNw_%WYJfw^Vt0^rw!7FWV^Y|;zdb^^7C@0Q0v(|wT%7V6$5B|l)Hl?z zX>8U9BaGJrBr*<>BZLHt6W-vkDI~<%X*9<9*BUfO*lq?eWaF?AD#FC6ZgC8CI3Jez z`k)Tg;~-IoOOF)4ylpYw-M0fElEr}Y-e?F>ak>{cin@=_`Rbexn8Vhy#a>)q4djs} zke~6V*1XrMv5AxC(o-nykMt@Re&U0|>wL&eRGeKrZrTgc>2il}rh>_TJi5CP>;?n> zM0B^ztvEW23{)39qg_0v{BX)~KR*`nlb>u6h(Dl5AQm@*XfQP0PGc9^C!>bE2+AVP z=pGpF90PDq(E)9X3*z^cC%xset{}NUOtDc_M zh;KqS>1H)~I_*Eut!nZ#o*%SBH!8BF6S0NP32{+jJx|9ceOnd+TX5GugGLM*ga-N7 z)8{BM`=DfC`&|Gs`2dus!iPg&Ek+Y}J_E`e`s+V@4jR5={LcA+T#|r21m$VicAud$ z8e=94Jpu0kUkX=e!&Q-e1r!F`)I-WVEeB!tj=)}`EU~XrXvE8)+ad;){!6+~8{Qx} z|2g6Zfqf0!GvJ;@oQz4lc;z;<#7p>V0~(%?U_tV#B82k`=n{v&3GO-4fV9#j#7Gyf zfGjNtJ}7!A{y8BM0(%SG^Wa|a2y5cJEMNtFBUnHez6tI}yiE+lx8U$mIkC9Uz76h0 zaG%9>J8>vWtvO|R zDYhiz0K^<8PrVN8M;zo8Z~83z@erhp1ua_aCmiF~_*wSTA!d0zCd1zK%W~c*;^p7S zXNH|cb^0@~lYWu##EeEo9JP4qTE zntu&bofP|x*Dfwz2Uf9qr9*U|%G_YT^;##~sEOgu?{GfVB%z#Ok3m^O6b8*;@B8(S z8gN&nHsEt6Xn-X91LZBLVJ!a`_{^uxZg}Y?Re$m<`!l$B@_Q}TO3+0FhvW0?FR>*- zxlXd?tzo)O+ f7mf(BNCF6t?$=TQ5=X$e*S!feZSNMITB-j5o1NCC literal 3719 zcmZ`*`;Qw}6+Sa#?<1MbZqiN2wq9bAo1`I4Gn>aYkA`+^Pj@Eq+EdSL+a(TDXFcB7 z7~8UE>Fx?Dq)Lr|f&>Eb6ygU0m3U}CLaHhi`a@fOfQlmiAa)>Dk^vTj}d(+f#gcze#G)fxbbxkd36^=%XT3jnv3|n6( zps8*a%PRSD@|KpbXf_8{G=y@;S}9*HkY8G*pz%Rot5nL7-<)b`t5-HE#Sv^g8D6A;@q~%B|ZjmB+e`hB|{)4RME_$iqTO0 zoX}fLjc2$PAX;XKMa_`0VVYPaEH~BM+Q^|h?#C?OA(tbv1)=HdYTmJQ_0rYV5E@Nf zzIJ7F;_Aw!a6+}wSt;zWAhkG}IC(yk1#E!0B`|s&#l53#qMx`lS-_^G*lc5+{uw0- z{uQ-|f1h@!23Q{3$Xg12t(=47!W`cKZ>9l$EP;0u*kRZfpgyzQNc#dj5I`1|@1}pd zoQ8cfW4^z@c?Xea?c8s2|iUuf1(d|!MwR68Q zMm?Q~HxQ_*QR9l(DayVj77^(6z zyvkD*cYxj9K9nr)MQ)tdI$Pa3cGrB;dMUCN+|as^v|io+^w&|XYp zUW_mpp4Hs$)Vv++&-T$IM&+2E+us+|sg`k(H-#@;5&ez;L9_G-C0z?LlXnZen^nETI1EB~UWZ zK-X*y8lf$EP25HMoFK3}FpJ0TI;t5S#xt2?tb`*xpODx}d3(4im(DXYz`YF0GBjHA zM(69bcC(&EtDC)xaJ?no1HC60%*LqWrG}4^Z}9bA3;cYU)|=8Ty5eR|WWoVmZ!;&b z)5$_t8^uD-qgZO~ZHz@>_W~bZ31eA|VnJ(oRE`ByivtQ@3^zy_7nILKS1F1@_#@nNx(c%ve5AHXbm)zg`Qvv?2ULfR?tN?>n;e-`|w zg9WSN5m~?vdKgV$3f}?$Twp{Qz6<-0%+%5@dk6e4f`1<8oz%W8wGJyQmo8tqy87I; z>uN4f&t+NMU#TvrW}1BuUQZ|C`u`_9WZwt>8F*a;bgN@DPI^(oRo_g}j3dow9<;R8YBqwSAV<;YD#I8P9U8ar((`1N#{#1Oufa)&O~Q+N9nar6 zNo4QFC&2H)U*XPW5sUPPpkR_LR3$BwWq%C)%h7J4hZ%YP6U=s|*%@kt7+c=M^QkHcW{SNJ$`xdxXa@Tr?0@utR*|*=KWAuf(*{Vhzfs-N z8fy9XC})1!4B{h}Tzwcs_7V7a;RRc)m7*5}9*#HJ$0JjMvPygHROTf6By@Sm?tazn zWD(!&yY(Q^e+2IS3A>?=@niTc>Jr@L{|5g#e6qg%&aKn1f{pzd_}6fZt^XgCwR&8w zK2JZW?0+$H|2p_Lf_4}8tD=lD%fh{dL#m!IxS#%uz7}3QAjl#Cke;+xrz0jF0F&O` NQyALLO}uKS{|9H{KED6} diff --git a/out/jwttest.hrb b/out/jwttest.hrb index ada2df05469e4a6558e14fab6c120dd3d2cc66d9..d7ad2b85bfe64e13fb78fd645e61f6859a4f57d0 100644 GIT binary patch delta 271 zcmey#cZZ+-fJcxM6NA7+_Sq8dt|1<-jxMf23f?12j6nK)z=gH;?DHgGd!3Hy61=qjWnmLw{a zr50tTWu~Suh%icesW^e9g}nWN(wTW>i8+}mU}ZpA6=$#- L@$90kZXmk>IA1*w delta 190 zcmcb^|C5jXfJcxM69eBw_SqBnN-(Nzye7p~Z^E#Fmm!`}gi*zeAtjI9F*w*YD8w}| z)X^uHp@o5gftP_v#R{k(o{2+7O~smF12;pKgul0fu0nBUdR}5lX;G>|S!z*cT4rhr zg9xLfzltqbjkvczP)%lDSz=CR3PLSViHaRq0asXJPHC#Df`S6bco8NQdx)@WML}wE PNoopElnE&62yztwX|^yX diff --git a/out/jwttest.log b/out/jwttest.log index f983178..45bfa51 100644 --- a/out/jwttest.log +++ b/out/jwttest.log @@ -1,8 +1,10 @@ -OK - signature verified -OK - signature verified -OK - signature verified -OK - signature verified -OK - signature verified -OK - signature verified -Token expired \ No newline at end of file +OK - data verified +OK - data verified +OK - data verified +OK - data verified +OK - data verified +OK - data verified +Token expired +OK - data verified +OK - data verified \ No newline at end of file diff --git a/src/jwt.prg b/src/jwt.prg index ee60298..6420757 100644 --- a/src/jwt.prg +++ b/src/jwt.prg @@ -16,53 +16,72 @@ CLASS JWT - DATA cSecret - DATA aHeader - DATA aPayload - DATA cError +HIDDEN: + + CLASSDATA cSecret + CLASSDATA aHeader + CLASSDATA aPayload + CLASSDATA cError + + METHOD Base64UrlEncode( cData ) + METHOD Base64UrlDecode( cData ) + METHOD ByteToString( cData ) + METHOD GetSignature( cHeader, cPayload, cSecret, cAlgorithm ) + +EXPORTED: METHOD New() CONSTRUCTOR // Header METHOD SetType( cType ) + METHOD GetType() INLINE ::aHeader[ 'typ' ] METHOD SetContentType( cContentType ) INLINE ::aHeader[ 'cty' ] := cContentType + METHOD GetContentType() INLINE ::aHeader[ 'cty' ] METHOD SetAlgorithm( cAlgorithm ) + METHOD GetAlgorithm() INLINE ::aHeader[ 'alg' ] // Payload METHOD SetIssuer( cIssuer ) INLINE ::aPayload[ 'iss' ] := cIssuer + METHOD GetIssuer() INLINE ::aPayload[ 'iss' ] METHOD SetSubject( cSubject ) INLINE ::aPayload[ 'sub' ] := cSubject + METHOD GetSubject() INLINE ::aPayload[ 'sub' ] METHOD SetAudience( cAudience ) INLINE ::aPayload[ 'aud' ] := cAudience + METHOD GetAudience() INLINE ::aPayload[ 'aud' ] METHOD SetExpration( nExpiration ) INLINE ::aPayload[ 'exp' ] := nExpiration + METHOD GetExpration() INLINE ::aPayload[ 'exp' ] METHOD SetNotBefore( nNotBefore ) INLINE ::aPayload[ 'nbf' ] := nNotBefore + METHOD GetNotBefore() INLINE ::aPayload[ 'nbf' ] METHOD SetIssuedAt( nIssuedAt ) INLINE ::aPayload[ 'iat' ] := nIssuedAt + METHOD GetIssuedAt() INLINE ::aPayload[ 'iat' ] METHOD SetJWTId( cJWTId ) INLINE ::aPayload[ 'jti' ] := cJWTId + METHOD GetJWTId() INLINE ::aPayload[ 'jti' ] + + // Payload methods + METHOD SetPayloadData( cKey, uValue ) INLINE ::aPayload[ cKey ] := uValue + METHOD GetPayloadData( cKey ) INLINE ::aPayload[ cKey ] // Secret METHOD SetSecret( cSecret ) INLINE ::cSecret := cSecret + METHOD GetSecret() INLINE ::cSecret + + // Error + METHOD GetError() INLINE ::cError - // Cleanup data + // Cleanup: aHeader, aPayload, cError, cSecret METHOD Reset() - // Encode a JWT + // Encode a JWT and return it METHOD Encode() // Decode a JWT METHOD Decode( cJWT, cSecret ) - // Payload methods - METHOD SetPayloadData( cKey, uValue ) INLINE ::aPayload[ cKey ] := uValue - METHOD GetPayloadData( cKey ) INLINE ::aPayload[ cKey ] - - // Getter internal data - METHOD GetPayload() INLINE ::aPayload - METHOD GetHeader() INLINE ::aHeader - METHOD GetError() INLINE ::cError + // Getter internal data with internal exposion + METHOD GetPayload() INLINE hb_hClone(::aPayload) + METHOD GetHeader() INLINE hb_hClone(::aHeader) - METHOD Base64UrlEncode( cData ) - METHOD Base64UrlDecode( cData ) - METHOD ByteToString( cData ) - METHOD GetSignature( cHeader, cPayload, cSecret, cAlgorithm ) - METHOD getposix() + // Helper method for expiration setting + METHOD GetSeconds() ENDCLASS @@ -74,12 +93,12 @@ RETU SELF METHOD SetType( cType ) CLASS JWT LOCAL bRet := .F. - if cType=="JWT" + IF cType=="JWT" ::aHeader[ 'typ' ] := cType - else + ELSE bRet := .F. ::cError := "Invalid type [" +cType +"]" - endif + ENDIF RETU bRet @@ -87,12 +106,12 @@ RETU bRet METHOD SetAlgorithm( cAlgorithm ) CLASS JWT LOCAL bRet := .F. - if cAlgorithm=="HS256" .OR. cAlgorithm=="HS384" .OR. cAlgorithm=="HS512" + IF cAlgorithm=="HS256" .OR. cAlgorithm=="HS384" .OR. cAlgorithm=="HS512" ::aHeader[ 'alg' ] := cAlgorithm - else + ELSE bRet := .F. ::cError := "Invalid algorithm [" +cAlgorithm +"]" - endif + ENDIF RETU bRet @@ -144,7 +163,7 @@ METHOD ByteToString( cData ) CLASS JWT RETU cRet - METHOD GetSignature( cHeader, cPayload, cSecret, cAlgorithm ) CLASS JWT +METHOD GetSignature( cHeader, cPayload, cSecret, cAlgorithm ) CLASS JWT LOCAL cSignature := "" DO CASE @@ -157,7 +176,7 @@ RETU cRet OTHERWISE ::cError := "INVALID ALGORITHM" ENDCASE - RETU cSignature +RETU cSignature METHOD Decode( cJWT, cSecret ) CLASS JWT @@ -194,7 +213,7 @@ METHOD Decode( cJWT, cSecret ) CLASS JWT // Check expiration IF hb_HHasKey(::aPayLoad,'exp') - IF ::aPayLoad[ 'exp' ] < ::getposix() + IF ::aPayLoad[ 'exp' ] < ::GetSeconds() ::cError := "Token expired" RETU .F. ENDIF @@ -202,11 +221,11 @@ METHOD Decode( cJWT, cSecret ) CLASS JWT RETU .T. -METHOD getposix() CLASS JWT +METHOD GetSeconds() CLASS JWT -LOCAL posixday := date() - STOD("19700101") -LOCAL cTime := time() -LOCAL posixsec := posixday * 24 * 60 * 60 + LOCAL posixday := date() - STOD("19700101") + LOCAL cTime := time() + LOCAL posixsec := posixday * 24 * 60 * 60 -return posixsec + (int(val(substr(cTime,1,2))) * 3600) + (int(val(substr(cTime,4.2))) * 60) + ( int(val(substr(cTime,7,2))) ) +RETU posixsec + (int(val(substr(cTime,1,2))) * 3600) + (int(val(substr(cTime,4.2))) * 60) + ( int(val(substr(cTime,7,2))) ) diff --git a/test/jwttest.prg b/test/jwttest.prg index 98e4133..3e1a450 100644 --- a/test/jwttest.prg +++ b/test/jwttest.prg @@ -49,6 +49,11 @@ AssertEquals( oJWT:Decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0 AssertEquals( oJWT:Decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ik1hdHRlbyBCYWNjYW4iLCJpYXQiOjE1MTYyMzkwMjIsImV4cCI6MTUxNjIzOTAyMn0.0T90m9fq8aOuiNbycTJxCf7BiQLw9xWXxe58-zV4RpY", "MySecret"), .F. ) ? oJWT:GetError() +// Check internal data exposion +AssertEquals(oJWT:GetHeader()['alg'], oJWT:GetAlgorithm()) +oJWT:GetHeader()['alg'] := 'dddd' +AssertEquals(oJWT:GetHeader()['alg'], oJWT:GetAlgorithm()) + hb_hrbUnload( handle ) RETU NIL @@ -56,9 +61,9 @@ RETU NIL function AssertEquals( uValue, uExpected ) IF uValue==uExpected - ? "OK - signature verified" + ? "OK - data verified" ELSE - ? "KO - invalid signature" + ? "KO - invalid data" ? "Value :", uValue ? "Expected:", uExpected ENDIF