@@ -36,6 +36,8 @@ var JWKSUrl = "https://firebaseappcheck.googleapis.com/v1beta/jwks"
3636
3737const appCheckIssuer = "https://firebaseappcheck.googleapis.com/"
3838
39+ const tokenVerificationUrlFormat = "https://firebaseappcheck.googleapis.com/v1beta/projects/%s:verifyAppCheckToken"
40+
3941var (
4042 // ErrIncorrectAlgorithm is returned when the token is signed with a non-RSA256 algorithm.
4143 ErrIncorrectAlgorithm = errors .New ("token has incorrect algorithm" )
@@ -70,9 +72,9 @@ type DecodedAppCheckToken struct {
7072
7173// Client is the interface for the Firebase App Check service.
7274type Client struct {
73- projectID string
74- jwks * keyfunc.JWKS
75- verifyAppCheckTokenURL string
75+ projectID string
76+ jwks * keyfunc.JWKS
77+ tokenVerificationUrl string
7678}
7779
7880// NewClient creates a new instance of the Firebase App Check Client.
@@ -90,9 +92,9 @@ func NewClient(ctx context.Context, conf *internal.AppCheckConfig) (*Client, err
9092 }
9193
9294 return & Client {
93- projectID : conf .ProjectID ,
94- jwks : jwks ,
95- verifyAppCheckTokenURL : fmt .Sprintf ("%sv1beta/projects/%s:verifyAppCheckToken" , appCheckIssuer , conf .ProjectID ),
95+ projectID : conf .ProjectID ,
96+ jwks : jwks ,
97+ tokenVerificationUrl : fmt .Sprintf (tokenVerificationUrlFormat , conf .ProjectID ),
9698 }, nil
9799}
98100
@@ -174,10 +176,34 @@ func (c *Client) VerifyToken(token string) (*DecodedAppCheckToken, error) {
174176 return & appCheckToken , nil
175177}
176178
177- // VerifyTokenWithReplayProtection checks the given App Check token as follows:
178- // - Uses VerifyToken to validate the given token as described. if verification failed, appropriate error will be returned.
179- // - Checks if the token token has been consumed. if already consumed the pointer to decoded token is returned with ErrTokenAlreadyConsumed.
180- func (c * Client ) VerifyTokenWithReplayProtection (token string ) (* DecodedAppCheckToken , error ) {
179+ // VerifyOneTimeToken verifies the given App Check token and consumes it, so that it cannot be consumed again.
180+ //
181+ // VerifyOneTimeToken considers an App Check token string to be valid if all the following conditions are met:
182+ // - The token string is a valid RS256 JWT.
183+ // - The JWT contains valid issuer (iss) and audience (aud) claims that match the issuerPrefix
184+ // and projectID of the tokenVerifier.
185+ // - The JWT contains a valid subject (sub) claim.
186+ // - The JWT is not expired, and it has been issued some time in the past.
187+ // - The JWT is signed by a Firebase App Check backend server as determined by the keySource.
188+ //
189+ // If any of the above conditions are not met, an error is returned, regardless whether the token was
190+ // previously consumed or not.
191+ //
192+ // This method currently only supports App Check tokens exchanged from the following attestation
193+ // providers:
194+ //
195+ // - Play Integrity API
196+ // - Apple App Attest
197+ // - Apple DeviceCheck (DCDevice tokens)
198+ // - reCAPTCHA Enterprise
199+ // - reCAPTCHA v3
200+ // - Custom providers
201+ //
202+ // App Check tokens exchanged from debug secrets are also supported. Calling this method on an
203+ // otherwise valid App Check token with an unsupported provider will cause an error to be returned.
204+ //
205+ // If the token was already consumed prior to this call, an error is returned.
206+ func (c * Client ) VerifyOneTimeToken (token string ) (* DecodedAppCheckToken , error ) {
181207 decodedAppCheckToken , err := c .VerifyToken (token )
182208
183209 if err != nil {
@@ -186,7 +212,7 @@ func (c *Client) VerifyTokenWithReplayProtection(token string) (*DecodedAppCheck
186212
187213 bodyReader := bytes .NewReader ([]byte (fmt .Sprintf (`{"app_check_token":%s}` , token )))
188214
189- resp , err := http .Post (c .verifyAppCheckTokenURL , "application/json" , bodyReader )
215+ resp , err := http .Post (c .tokenVerificationUrl , "application/json" , bodyReader )
190216
191217 if err != nil {
192218 return nil , err
@@ -203,7 +229,7 @@ func (c *Client) VerifyTokenWithReplayProtection(token string) (*DecodedAppCheck
203229 }
204230
205231 if rb .AlreadyConsumed {
206- return decodedAppCheckToken , ErrTokenAlreadyConsumed
232+ return nil , ErrTokenAlreadyConsumed
207233 }
208234
209235 return decodedAppCheckToken , nil
0 commit comments