@@ -68,6 +68,122 @@ public async Task ValidateAsync_InvalidToken_RejectsWithInvalidRequest(
6868 Assert . Equal ( "invalid_request" , context . Result . Error ) ;
6969 }
7070
71+ [ Theory ]
72+ [ BitAutoData ( "" , "{}" ) ]
73+ [ BitAutoData ( " " , "{}" ) ]
74+ [ BitAutoData ( "test-token" , "" ) ]
75+ [ BitAutoData ( "test-token" , " " ) ]
76+ public async Task ValidateAsync_EmptyOrWhitespaceParameter_RejectsWithInvalidGrant (
77+ string token ,
78+ string deviceResponse ,
79+ [ AuthFixtures . ValidatedTokenRequest ] ValidatedTokenRequest tokenRequest ,
80+ SutProvider < WebAuthnGrantValidator > sutProvider )
81+ {
82+ // Arrange - one of token / deviceResponse is empty or whitespace
83+ var context = CreateContext ( tokenRequest , token , deviceResponse ) ;
84+
85+ // Act
86+ await sutProvider . Sut . ValidateAsync ( context ) ;
87+
88+ // Assert
89+ Assert . Equal ( "invalid_grant" , context . Result . Error ) ;
90+ sutProvider . GetDependency < IDataProtectorTokenFactory < WebAuthnLoginAssertionOptionsTokenable > > ( )
91+ . DidNotReceive ( )
92+ . TryUnprotect ( Arg . Any < string > ( ) , out Arg . Any < WebAuthnLoginAssertionOptionsTokenable > ( ) ) ;
93+ }
94+
95+ [ Theory , BitAutoData ]
96+ public async Task ValidateAsync_DeviceResponseDeserializesToNull_RejectsWithInvalidRequest (
97+ [ AuthFixtures . ValidatedTokenRequest ] ValidatedTokenRequest tokenRequest ,
98+ SutProvider < WebAuthnGrantValidator > sutProvider )
99+ {
100+ // Arrange
101+ var options = new AssertionOptions { Challenge = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ] } ;
102+ var tokenable = new WebAuthnLoginAssertionOptionsTokenable (
103+ WebAuthnLoginAssertionOptionsScope . Authentication , options ) ;
104+
105+ var context = CreateContext ( tokenRequest , deviceResponse : "null" ) ;
106+
107+ sutProvider . GetDependency < IDataProtectorTokenFactory < WebAuthnLoginAssertionOptionsTokenable > > ( )
108+ . TryUnprotect ( Arg . Any < string > ( ) , out Arg . Any < WebAuthnLoginAssertionOptionsTokenable > ( ) )
109+ . Returns ( x =>
110+ {
111+ x [ 1 ] = tokenable ;
112+ return true ;
113+ } ) ;
114+
115+ // Act
116+ await sutProvider . Sut . ValidateAsync ( context ) ;
117+
118+ // Assert
119+ Assert . Equal ( "invalid_request" , context . Result . Error ) ;
120+ await sutProvider . GetDependency < IAssertWebAuthnLoginCredentialCommand > ( )
121+ . DidNotReceive ( )
122+ . AssertWebAuthnLoginCredential ( Arg . Any < AssertionOptions > ( ) , Arg . Any < AuthenticatorAssertionRawResponse > ( ) ) ;
123+ }
124+
125+ [ Theory , BitAutoData ]
126+ public async Task ValidateAsync_TokenWithWrongScope_RejectsWithInvalidRequest (
127+ [ AuthFixtures . ValidatedTokenRequest ] ValidatedTokenRequest tokenRequest ,
128+ SutProvider < WebAuthnGrantValidator > sutProvider )
129+ {
130+ // Arrange
131+ var options = new AssertionOptions { Challenge = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ] } ;
132+ var tokenable = new WebAuthnLoginAssertionOptionsTokenable (
133+ WebAuthnLoginAssertionOptionsScope . PrfRegistration , options ) ;
134+
135+ var context = CreateContext ( tokenRequest ) ;
136+
137+ sutProvider . GetDependency < IDataProtectorTokenFactory < WebAuthnLoginAssertionOptionsTokenable > > ( )
138+ . TryUnprotect ( Arg . Any < string > ( ) , out Arg . Any < WebAuthnLoginAssertionOptionsTokenable > ( ) )
139+ . Returns ( x =>
140+ {
141+ x [ 1 ] = tokenable ;
142+ return true ;
143+ } ) ;
144+
145+ // Act
146+ await sutProvider . Sut . ValidateAsync ( context ) ;
147+
148+ // Assert
149+ Assert . Equal ( "invalid_request" , context . Result . Error ) ;
150+ await sutProvider . GetDependency < IAssertWebAuthnLoginCredentialCommand > ( )
151+ . DidNotReceive ( )
152+ . AssertWebAuthnLoginCredential ( Arg . Any < AssertionOptions > ( ) , Arg . Any < AuthenticatorAssertionRawResponse > ( ) ) ;
153+ }
154+
155+ [ Theory , BitAutoData ]
156+ public async Task ValidateAsync_TokenWithNullOptions_RejectsWithInvalidRequest (
157+ [ AuthFixtures . ValidatedTokenRequest ] ValidatedTokenRequest tokenRequest ,
158+ SutProvider < WebAuthnGrantValidator > sutProvider )
159+ {
160+ // Arrange
161+ var tokenable = new WebAuthnLoginAssertionOptionsTokenable
162+ {
163+ Scope = WebAuthnLoginAssertionOptionsScope . Authentication ,
164+ Options = null ,
165+ } ;
166+
167+ var context = CreateContext ( tokenRequest ) ;
168+
169+ sutProvider . GetDependency < IDataProtectorTokenFactory < WebAuthnLoginAssertionOptionsTokenable > > ( )
170+ . TryUnprotect ( Arg . Any < string > ( ) , out Arg . Any < WebAuthnLoginAssertionOptionsTokenable > ( ) )
171+ . Returns ( x =>
172+ {
173+ x [ 1 ] = tokenable ;
174+ return true ;
175+ } ) ;
176+
177+ // Act
178+ await sutProvider . Sut . ValidateAsync ( context ) ;
179+
180+ // Assert
181+ Assert . Equal ( "invalid_request" , context . Result . Error ) ;
182+ await sutProvider . GetDependency < IAssertWebAuthnLoginCredentialCommand > ( )
183+ . DidNotReceive ( )
184+ . AssertWebAuthnLoginCredential ( Arg . Any < AssertionOptions > ( ) , Arg . Any < AuthenticatorAssertionRawResponse > ( ) ) ;
185+ }
186+
71187 [ Theory , BitAutoData ]
72188 public async Task ValidateAsync_ValidToken_CallsAssertCommand (
73189 [ AuthFixtures . ValidatedTokenRequest ] ValidatedTokenRequest tokenRequest ,
0 commit comments