1*4b347fccSAndrew Lindesay /* 2*4b347fccSAndrew Lindesay * Copyright 2023, Andrew Lindesay <apl@lindesay.co.nz>. 3*4b347fccSAndrew Lindesay * All rights reserved. Distributed under the terms of the MIT License. 4*4b347fccSAndrew Lindesay */ 5*4b347fccSAndrew Lindesay 6*4b347fccSAndrew Lindesay 7*4b347fccSAndrew Lindesay #include "JwtTokenHelper.h" 8*4b347fccSAndrew Lindesay 9*4b347fccSAndrew Lindesay #include "DataIOUtils.h" 10*4b347fccSAndrew Lindesay #include "Json.h" 11*4b347fccSAndrew Lindesay #include "JsonMessageWriter.h" 12*4b347fccSAndrew Lindesay 13*4b347fccSAndrew Lindesay #include <ctype.h> 14*4b347fccSAndrew Lindesay 15*4b347fccSAndrew Lindesay 16*4b347fccSAndrew Lindesay /*static*/ bool IsValid(const BString & value)17*4b347fccSAndrew LindesayJwtTokenHelper::IsValid(const BString& value) 18*4b347fccSAndrew Lindesay { 19*4b347fccSAndrew Lindesay int countDots = 0; 20*4b347fccSAndrew Lindesay 21*4b347fccSAndrew Lindesay for (int i = 0; i < value.Length(); i++) { 22*4b347fccSAndrew Lindesay char ch = value[i]; 23*4b347fccSAndrew Lindesay 24*4b347fccSAndrew Lindesay if ('.' == ch) 25*4b347fccSAndrew Lindesay countDots++; 26*4b347fccSAndrew Lindesay else { 27*4b347fccSAndrew Lindesay if (!_IsBase64(ch)) 28*4b347fccSAndrew Lindesay return false; 29*4b347fccSAndrew Lindesay } 30*4b347fccSAndrew Lindesay } 31*4b347fccSAndrew Lindesay 32*4b347fccSAndrew Lindesay return 2 == countDots; 33*4b347fccSAndrew Lindesay } 34*4b347fccSAndrew Lindesay 35*4b347fccSAndrew Lindesay 36*4b347fccSAndrew Lindesay /*! A JWT token is split into three parts separated by a '.' character. The 37*4b347fccSAndrew Lindesay middle section is a base-64 encoded string and within the string is JSON 38*4b347fccSAndrew Lindesay structured data. The JSON data contains key-value pairs which carry data 39*4b347fccSAndrew Lindesay about the token. This method will take a JWT token, will find the middle 40*4b347fccSAndrew Lindesay section and will parse the claims into the supplied 'message' parameter. 41*4b347fccSAndrew Lindesay */ 42*4b347fccSAndrew Lindesay 43*4b347fccSAndrew Lindesay /*static*/ status_t ParseClaims(const BString & token,BMessage & message)44*4b347fccSAndrew LindesayJwtTokenHelper::ParseClaims(const BString& token, BMessage& message) 45*4b347fccSAndrew Lindesay { 46*4b347fccSAndrew Lindesay int firstDot = token.FindFirst('.'); 47*4b347fccSAndrew Lindesay 48*4b347fccSAndrew Lindesay if (firstDot == B_ERROR) 49*4b347fccSAndrew Lindesay return B_BAD_VALUE; 50*4b347fccSAndrew Lindesay 51*4b347fccSAndrew Lindesay // find the end of the first section by looking for the next dot. 52*4b347fccSAndrew Lindesay 53*4b347fccSAndrew Lindesay int secondDot = token.FindFirst('.', firstDot + 1); 54*4b347fccSAndrew Lindesay 55*4b347fccSAndrew Lindesay if (secondDot == B_ERROR) 56*4b347fccSAndrew Lindesay return B_BAD_VALUE; 57*4b347fccSAndrew Lindesay 58*4b347fccSAndrew Lindesay BMemoryIO memoryIo(&(token.String())[firstDot + 1], (secondDot - firstDot) - 1); 59*4b347fccSAndrew Lindesay Base64DecodingDataIO base64DecodingIo(&memoryIo, '-', '_'); 60*4b347fccSAndrew Lindesay BJsonMessageWriter writer(message); 61*4b347fccSAndrew Lindesay BJson::Parse(&base64DecodingIo, &writer); 62*4b347fccSAndrew Lindesay 63*4b347fccSAndrew Lindesay return writer.ErrorStatus(); 64*4b347fccSAndrew Lindesay } 65*4b347fccSAndrew Lindesay 66*4b347fccSAndrew Lindesay 67*4b347fccSAndrew Lindesay /*! Note this is base64 "url" standard that disallows "/" and "+" and instead 68*4b347fccSAndrew Lindesay uses "-" and "_". 69*4b347fccSAndrew Lindesay */ 70*4b347fccSAndrew Lindesay 71*4b347fccSAndrew Lindesay /*static*/ bool _IsBase64(char ch)72*4b347fccSAndrew LindesayJwtTokenHelper::_IsBase64(char ch) 73*4b347fccSAndrew Lindesay { 74*4b347fccSAndrew Lindesay return isalnum(ch) 75*4b347fccSAndrew Lindesay || '=' == ch 76*4b347fccSAndrew Lindesay || '-' == ch 77*4b347fccSAndrew Lindesay || '_' == ch; 78*4b347fccSAndrew Lindesay } 79