@@ -594,8 +594,21 @@ public async Task<Details> LoadDetails()
594594 // TODO: app token?
595595 // public sealed record App(string Token) : Authenticated;
596596
597+ internal delegate Authenticated Parser (
598+ string tokenStr ,
599+ bool isAuthenticated ,
600+ ApplicationMetadata appMetadata ,
601+ Func < string ? > getSelectedParty ,
602+ Func < int , Task < UserProfile ? > > getUserProfile ,
603+ Func < int , Task < Party ? > > lookupUserParty ,
604+ Func < string , Task < Party > > lookupOrgParty ,
605+ Func < int , Task < List < Party > ? > > getPartyList ,
606+ Func < int , int , Task < bool ? > > validateSelectedParty
607+ ) ;
608+
597609 internal static Authenticated FromLocalTest (
598610 string tokenStr ,
611+ bool isAuthenticated ,
599612 ApplicationMetadata appMetadata ,
600613 Func < string ? > getSelectedParty ,
601614 Func < int , Task < UserProfile ? > > getUserProfile ,
@@ -607,7 +620,7 @@ internal static Authenticated FromLocalTest(
607620 {
608621 var context = new ParseContext (
609622 tokenStr ,
610- ! string . IsNullOrWhiteSpace ( tokenStr ) ,
623+ isAuthenticated ,
611624 appMetadata ,
612625 getSelectedParty ,
613626 getUserProfile ,
@@ -622,14 +635,15 @@ internal static Authenticated FromLocalTest(
622635 var handler = new JwtSecurityTokenHandler ( ) ;
623636 var token = handler . ReadJwtToken ( tokenStr ) ;
624637
625- context . TokenIssuer = TokenIssuer . Altinn ;
626- context . IsExchanged = true ;
627-
628638 context . ReadClaims ( token ) ;
629639
640+ context . TokenIssuer = context . OrgNoClaim . Exists ? TokenIssuer . Maskinporten : TokenIssuer . Altinn ;
641+ context . IsExchanged =
642+ context . TokenIssuer == TokenIssuer . Maskinporten || context . TokenIssuer == TokenIssuer . IDporten ;
630643 context . Scopes = context . ScopeClaim . IsValidString ( out var scopeClaimValue )
631644 ? new Scopes ( scopeClaimValue )
632645 : new Scopes ( null ) ;
646+ context . IsInAltinnPortal = context . UserIdClaim . Exists ;
633647
634648 int ? partyId = null ;
635649 if ( context . PartyIdClaim . Exists )
@@ -709,6 +723,7 @@ internal record struct ParseContext(
709723 )
710724 {
711725 public TokenClaim IssuerClaim = default ;
726+ public TokenClaim ActualIssuerClaim = default ;
712727 public TokenClaim AuthLevelClaim = default ;
713728 public TokenClaim AuthMethodClaim = default ;
714729 public TokenClaim ScopeClaim = default ;
@@ -804,6 +819,7 @@ public void ReadClaims(JwtSecurityToken token)
804819 foreach ( var claim in token . Payload )
805820 {
806821 TryAssign ( claim , JwtClaimTypes . Issuer , ref IssuerClaim ) ;
822+ TryAssign ( claim , "actual_iss" , ref ActualIssuerClaim ) ;
807823 TryAssign ( claim , AltinnCoreClaimTypes . AuthenticationLevel , ref AuthLevelClaim ) ;
808824 TryAssign ( claim , AltinnCoreClaimTypes . AuthenticateMethod , ref AuthMethodClaim ) ;
809825 TryAssign ( claim , JwtClaimTypes . Scope , ref ScopeClaim ) ;
@@ -867,7 +883,13 @@ internal static Authenticated From(
867883 context . Scopes = context . ScopeClaim . IsValidString ( out var scopeClaimValue )
868884 ? new Scopes ( scopeClaimValue )
869885 : new Scopes ( null ) ;
870- context . IsInAltinnPortal = context . Scopes . HasScope ( "altinn:portal/enduser" ) ;
886+ context . IsInAltinnPortal =
887+ context . Scopes . HasScope ( "altinn:portal/enduser" )
888+ || (
889+ context . ActualIssuerClaim . IsValidString ( out var actualIssuer )
890+ && actualIssuer == "altinn-test-tools"
891+ && context . UserIdClaim . Exists
892+ ) ;
871893
872894 context . ResolveIssuer ( ) ;
873895
@@ -918,12 +940,13 @@ static Authenticated NewUser(ref ParseContext context)
918940 {
919941 if ( ! context . UserIdClaim . Exists )
920942 throw new AuthenticationContextException ( "Missing user ID claim for user token" ) ;
921- if ( ! context . UserIdClaim . IsValidString ( out var userIdStr ) )
943+
944+ if ( ! context . UserIdClaim . IsValidInt ( out var userId ) )
945+ {
922946 throw new AuthenticationContextException (
923947 $ "Invalid user ID claim value for user token: { context . UserIdClaim . Value } "
924948 ) ;
925- if ( ! int . TryParse ( userIdStr , CultureInfo . InvariantCulture , out var userId ) )
926- throw new AuthenticationContextException ( $ "Invalid user ID claim value for user token: { userIdStr } ") ;
949+ }
927950
928951 if ( ! context . PartyIdClaim . Exists )
929952 throw new AuthenticationContextException ( "Missing party ID for user token" ) ;
@@ -941,7 +964,13 @@ static Authenticated NewUser(ref ParseContext context)
941964 if ( ! context . UsernameClaim . IsValidString ( out var usernameClaimValue ) )
942965 throw new AuthenticationContextException ( "Missing username claim for self-identified user token" ) ;
943966
944- return new SelfIdentifiedUser ( usernameClaimValue , userId , partyId . Value , authMethodClaimValue , ref context ) ;
967+ return new SelfIdentifiedUser (
968+ usernameClaimValue ,
969+ userId . Value ,
970+ partyId . Value ,
971+ authMethodClaimValue ,
972+ ref context
973+ ) ;
945974 }
946975
947976 int selectedPartyId = partyId . Value ;
@@ -953,7 +982,7 @@ static Authenticated NewUser(ref ParseContext context)
953982 selectedPartyId = selectedParty ;
954983 }
955984
956- return new User ( userId , partyId . Value , authLevel , authMethodClaimValue , selectedPartyId , ref context ) ;
985+ return new User ( userId . Value , partyId . Value , authLevel , authMethodClaimValue , selectedPartyId , ref context ) ;
957986 }
958987
959988 static Org NewOrg ( ref ParseContext context )
@@ -971,17 +1000,7 @@ static SystemUser NewSystemUser(ref ParseContext context)
9711000 {
9721001 if ( ! context . AuthorizationDetailsClaim . IsJson ( out var json ) )
9731002 throw new AuthenticationContextException ( $ "Invalid authorization details claim value for token: { json } ") ;
974- var authorizationDetails = json . Value . ValueKind switch
975- {
976- JsonValueKind . Object => JsonSerializer . Deserialize < AuthorizationDetailsClaim > ( json . Value ) ,
977- JsonValueKind . Array when json . Value . GetArrayLength ( ) == 1 => JsonSerializer
978- . Deserialize < AuthorizationDetailsClaim [ ] > ( json . Value )
979- ? [ 0 ] ,
980- _ => throw new AuthenticationContextException (
981- "Invalid authorization details claim value for systemuser token: "
982- + context . AuthorizationDetailsClaim . Value
983- ) ,
984- } ;
1003+ var authorizationDetails = AuthorizationDetailsClaim . Parse ( json . Value ) ;
9851004 if ( authorizationDetails is null )
9861005 throw new AuthenticationContextException ( "Invalid authorization details claim value for systemuser token" ) ;
9871006 if ( authorizationDetails is not SystemUserAuthorizationDetailsClaim systemUser )
@@ -1061,10 +1080,25 @@ public bool IsValidInt([NotNullWhen(true)] out int? integer)
10611080 {
10621081 integer = null ;
10631082
1064- if ( Type is not null && Value is int intValue )
1083+ if ( Type is not null )
10651084 {
1066- integer = intValue ;
1067- return true ;
1085+ if ( Value is int intValue )
1086+ {
1087+ integer = intValue ;
1088+ return true ;
1089+ }
1090+ // We parse tokens from various different sources:
1091+ // * altinn-authentication
1092+ // * localtest
1093+ // * AltinnTesTools
1094+ // * TestAuthentication (this repo)
1095+ // All of them have slight differences in how values encoded in the JWT payload,
1096+ // so that's why we are flexible here...
1097+ if ( Value is string strValue && int . TryParse ( strValue , CultureInfo . InvariantCulture , out intValue ) )
1098+ {
1099+ integer = intValue ;
1100+ return true ;
1101+ }
10681102 }
10691103
10701104 return false ;
@@ -1086,7 +1120,22 @@ public bool IsJson([NotNullWhen(true)] out JsonElement? json)
10861120
10871121 [ JsonPolymorphic ( TypeDiscriminatorPropertyName = "type" ) ]
10881122 [ JsonDerivedType ( typeof ( SystemUserAuthorizationDetailsClaim ) , typeDiscriminator : "urn:altinn:systemuser" ) ]
1089- internal record AuthorizationDetailsClaim ( ) ;
1123+ internal record AuthorizationDetailsClaim ( )
1124+ {
1125+ public static AuthorizationDetailsClaim ? Parse ( JsonElement json )
1126+ {
1127+ return json . ValueKind switch
1128+ {
1129+ JsonValueKind . Object => JsonSerializer . Deserialize < AuthorizationDetailsClaim > ( json ) ,
1130+ JsonValueKind . Array when json . GetArrayLength ( ) == 1 => JsonSerializer
1131+ . Deserialize < AuthorizationDetailsClaim [ ] > ( json )
1132+ ? [ 0 ] ,
1133+ _ => throw new AuthenticationContextException (
1134+ "Invalid authorization details claim value for systemuser token: " + json
1135+ ) ,
1136+ } ;
1137+ }
1138+ }
10901139
10911140 internal sealed record SystemUserAuthorizationDetailsClaim (
10921141 [ property: JsonPropertyName ( "systemuser_id" ) ] IReadOnlyList < Guid > SystemUserId ,
0 commit comments