22// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
33// ------------------------------------------------------------------------------
44
5+ using Microsoft . Graph . PowerShell . Authentication . Core . Models ;
6+ using Microsoft . Graph . PowerShell . Authentication . Models ;
57using Microsoft . Identity . Client ;
68using Newtonsoft . Json ;
79using System ;
810using System . Globalization ;
9- using System . IdentityModel . Tokens . Jwt ;
11+ using System . Text ;
1012
1113namespace Microsoft . Graph . PowerShell . Authentication . Core . Utilities
1214{
@@ -23,7 +25,7 @@ internal static class JwtHelpers
2325 /// <param name="authContext">An <see cref="IAuthContext"/> to store JWT claims in.</param>
2426 internal static void DecodeJWT ( string jwToken , IAccount account , ref IAuthContext authContext )
2527 {
26- var jwtPayload = DecodeToObject < Models . JwtPayload > ( jwToken ) ;
28+ var jwtPayload = DecodeToObject < JwtPayload > ( jwToken ) ;
2729 if ( authContext . AuthType == AuthenticationType . UserProvidedAccessToken )
2830 {
2931 if ( jwtPayload == null )
@@ -50,22 +52,6 @@ internal static void DecodeJWT(string jwToken, IAccount account, ref IAuthContex
5052 authContext . Account = jwtPayload ? . Upn ?? account ? . Username ;
5153 }
5254
53- /// <summary>
54- /// Decodes a JWT token by extracting claims from the payload.
55- /// </summary>
56- /// <param name="jwToken">A JWT string.</param>
57- internal static string Decode ( string jwToken )
58- {
59- JwtSecurityTokenHandler jwtHandler = new JwtSecurityTokenHandler ( ) ;
60- if ( jwtHandler . CanReadToken ( jwToken ) )
61- {
62- JwtSecurityToken token = jwtHandler . ReadJwtToken ( jwToken ) ;
63- return token . Payload . SerializeToJson ( ) ;
64- } else {
65- return null ;
66- }
67- }
68-
6955 /// <summary>
7056 /// Decodes a JWT token by extracting claims from the payload to an object of type T.
7157 /// </summary>
@@ -75,17 +61,49 @@ internal static T DecodeToObject<T>(string jwtString)
7561 {
7662 try
7763 {
78- string decodedJWT = Decode ( jwtString ) ;
79- if ( decodedJWT == null )
64+ var decodedJWT = DecodeJWT ( jwtString ) ;
65+ if ( string . IsNullOrWhiteSpace ( decodedJWT ? . Payload ) )
8066 return default ;
81- return JsonConvert . DeserializeObject < T > ( decodedJWT ) ;
67+ return JsonConvert . DeserializeObject < T > ( decodedJWT . Payload ) ;
8268 }
8369 catch ( Exception ex )
8470 {
8571 throw new AuthenticationException ( ErrorConstants . Message . InvalidJWT , ex ) ;
8672 }
8773 }
8874
75+ internal static JwtContent DecodeJWT ( string jwtString )
76+ {
77+ // See https://tools.ietf.org/html/rfc7519
78+ if ( string . IsNullOrWhiteSpace ( jwtString ) || ! jwtString . Contains ( "." ) || ! jwtString . StartsWith ( "eyJ" ) )
79+ throw new ArgumentException ( "Invalid JSON Web Token (JWT)." ) ;
80+
81+ var jwtSegments = jwtString . Split ( '.' ) ;
82+
83+ if ( jwtSegments . Length <= 1 )
84+ throw new ArgumentException ( "Invalid JWT. JWT does not have a payload." ) ;
85+
86+ // Header
87+ var jwtHeader = DecodeJwtSegment ( jwtSegments [ 0 ] ) ;
88+
89+ // Payload
90+ var jwtPayload = DecodeJwtSegment ( jwtSegments [ 1 ] ) ;
91+
92+ return new JwtContent { Header = jwtHeader , Payload = jwtPayload } ;
93+ }
94+
95+ private static string DecodeJwtSegment ( string jwtSegment )
96+ {
97+ jwtSegment = jwtSegment . Replace ( '-' , '+' ) . Replace ( '_' , '/' ) ;
98+ // Fixes padding by adding '=' until header length modulus 4 equals 0.
99+ while ( ( jwtSegment . Length % 4 ) != 0 )
100+ jwtSegment += "=" ;
101+
102+ var jwtTokenBytes = Convert . FromBase64String ( jwtSegment ) ;
103+ var jwtSegmentInJson = Encoding . UTF8 . GetString ( jwtTokenBytes ) ;
104+ return jwtSegmentInJson ;
105+ }
106+
89107 /// <summary>
90108 /// Converts a DateTime to Unix timestamp in seconds past epoch.
91109 /// </summary>
0 commit comments