Category: Expert Guide

How does a JWT decoder verify a token?

# The Ultimate Authoritative Guide to JWT Decoding and Verification As a Principal Software Engineer, I understand the critical importance of secure and reliable authentication and authorization mechanisms in modern applications. JSON Web Tokens (JWTs) have become a de facto standard for this purpose, offering a compact and self-contained way to transmit information between parties. However, the true strength of JWTs lies not just in their creation, but in their robust verification. This guide will delve deep into the intricate process of how a JWT decoder verifies a token, empowering you with the knowledge to implement and manage JWT security with confidence. Our core tool for exploration and practical demonstration will be the widely adopted `jwt-decoder` library, a testament to the open-source community's commitment to secure and accessible cryptographic tools. ## Executive Summary At its core, a JWT decoder verifies a token by meticulously examining its three distinct parts: the Header, the Payload, and the Signature. The process is not a simple deserialization; it's a rigorous cryptographic validation. The decoder first decodes the Base64Url-encoded Header and Payload to inspect their contents. Crucially, it then uses the algorithm specified in the Header (e.g., HS256, RS256) and a pre-shared secret key (for symmetric algorithms) or a public key (for asymmetric algorithms) to *recreate* the signature based on the decoded Header and Payload. This recreated signature is then compared byte-for-byte with the signature provided in the token. If they match, and if any expiration or other validation rules are met, the token is deemed valid. This multi-layered approach ensures that the token's integrity has not been compromised and that it originated from a trusted source. Failure at any stage of this verification process renders the token invalid, preventing unauthorized access or data manipulation. ## Deep Technical Analysis: The Anatomy of JWT Verification To truly understand JWT verification, we must dissect each component and the cryptographic principles that underpin their validation. A JWT is composed of three parts, separated by dots (`.`): 1. **Header:** Contains metadata about the token, including the signing algorithm and token type. 2. **Payload:** Contains the claims, which are statements about an entity (typically, the user) and additional data. 3. **Signature:** Used to verify that the sender of the JWT is who it says it is and to ensure that the message wasn't changed along the way. The verification process involves a series of steps that leverage these components and cryptographic primitives. ### 1. Base64Url Decoding The Header and Payload are encoded using Base64Url, a URL-and-filename-safe variant of Base64 encoding. The `jwt-decoder` library, like any compliant decoder, will first perform this decoding. * **Header Decoding:** The first part of the JWT is Base64Url decoded to reveal a JSON object. This object *must* contain at least the `alg` (algorithm) parameter, which specifies the cryptographic algorithm used to sign the token. It may also contain `typ` (type), typically set to "JWT". json { "alg": "HS256", "typ": "JWT" } * **Payload Decoding:** The second part is also Base64Url decoded to reveal another JSON object containing the claims. Claims can be registered (e.g., `iss` for issuer, `exp` for expiration time, `sub` for subject), public, or private. json { "sub": "1234567890", "name": "John Doe", "iat": 1516239022 } ### 2. Algorithm Identification and Key Retrieval The `jwt-decoder` library inspects the `alg` parameter from the decoded Header. This is a critical step as it dictates the verification strategy. * **Symmetric Algorithms (e.g., HS256, HS512):** For symmetric algorithms, the same secret key is used for both signing and verification. The decoder needs access to this pre-shared secret. This secret is typically configured in the application or service that is responsible for verifying the token. * **Asymmetric Algorithms (e.g., RS256, ES256):** For asymmetric algorithms, a public/private key pair is used. The private key is used for signing, and the corresponding public key is used for verification. The `jwt-decoder` will need access to the public key that corresponds to the private key used by the issuer. This public key can be provided directly, retrieved from a JWKS (JSON Web Key Set) endpoint, or obtained through other secure means. ### 3. Signature Verification: The Cryptographic Heartbeat This is the most crucial step. The decoder reconstructs the "signing input" and then uses the identified algorithm and the appropriate key to generate a new signature. * **Constructing the Signing Input:** The signing input is formed by concatenating the Base64Url-encoded Header and the Base64Url-encoded Payload, separated by a dot (`.`). signing_input = base64UrlEncode(header) + "." + base64UrlEncode(payload) * **Generating the Expected Signature:** The decoder then applies the algorithm specified in the Header to the `signing_input` using the retrieved secret (for symmetric) or public key (for asymmetric). * **For HS256 (HMAC SHA-256):** expected_signature = HMAC_SHA256(secret_key, signing_input) The `secret_key` is the pre-shared secret. * **For RS256 (RSA Signature with SHA-256):** expected_signature = RSA_SIGN(public_key, signing_input, SHA-256) The `public_key` is the RSA public key. The signing operation here is actually verifying the signature provided by the issuer using their private key. The process is: the issuer signs the `signing_input` with their private key. The decoder uses the public key to verify this signature. * **For ES256 (ECDSA with SHA-256):** expected_signature = ECDSA_SIGN(public_key, signing_input, SHA-256) Similar to RS256, the public key is used to verify the ECDSA signature created by the issuer's private key. The output of these cryptographic operations is a binary signature. * **Comparing Signatures:** The generated `expected_signature` (which is also Base64Url encoded by the `jwt-decoder` library for comparison with the provided token's signature) is then compared byte-for-byte with the third part of the JWT, the provided signature. **If `expected_signature` == `provided_signature`:** The signature is valid, meaning the token has not been tampered with since it was signed, and it was indeed signed by the party possessing the corresponding private key (or the shared secret). **If they do not match:** The token is invalid and is rejected. ### 4. Claim Validation Beyond the signature, a robust JWT decoder also validates the claims within the payload. This ensures the token is not only authentic but also still relevant and authorized for use. * **Expiration Time (`exp`):** This is a mandatory claim for security. The decoder checks if the current time is *before* the `exp` timestamp. If the token has expired, it's considered invalid. python import time if payload.get("exp") is not None and payload["exp"] < time.time(): raise ExpiredSignatureError("Token has expired") * **Not Before (`nbf`):** This claim indicates the time before which the token must not be accepted. The decoder checks if the current time is *after* the `nbf` timestamp. python if payload.get("nbf") is not None and payload["nbf"] > time.time(): raise ImmatureSignatureError("Token cannot be used yet") * **Issued At (`iat`):** This claim indicates the time at which the JWT was issued. While not strictly a validation check, it's useful for auditing and debugging. * **Audience (`aud`):** This claim identifies the intended recipient of the JWT. The decoder can verify if the token is intended for the current service or application. If the `aud` claim is a string, it must match the expected audience. If it's an array, the expected audience must be present in the array. * **Issuer (`iss`):** This claim identifies the principal that issued the JWT. The decoder can verify if the token was issued by a trusted authority. * **Subject (`sub`):** This claim identifies the principal that is the subject of the JWT. * **Custom Claims:** Any custom claims defined by the application can also be validated according to specific business logic. ### 5. Algorithm Not Allowed (`alg: "none"`) A critical security vulnerability arises if a token is signed with the `alg: "none"` header. This indicates that no signature was applied. A naive decoder might accept such a token without verification. A secure `jwt-decoder` will explicitly disallow this algorithm unless explicitly configured otherwise (which is highly discouraged). ### Summary of the Verification Flow 1. **Split Token:** Separate the JWT into its three parts: Header, Payload, Signature. 2. **Decode Header:** Base64Url decode the Header to get a JSON object. 3. **Identify Algorithm:** Extract the `alg` parameter from the Header. 4. **Retrieve Key:** Based on the `alg`, retrieve the appropriate secret key or public key. 5. **Decode Payload:** Base64Url decode the Payload to get a JSON object containing claims. 6. **Construct Signing Input:** Concatenate Base64Url-encoded Header and Payload with a dot. 7. **Generate Expected Signature:** Cryptographically compute the signature of the signing input using the identified algorithm and retrieved key. 8. **Compare Signatures:** Compare the generated signature (Base64Url encoded) with the provided signature. If they match, proceed. 9. **Validate Claims:** Perform checks on claims like `exp`, `nbf`, `aud`, `iss`, etc. 10. **Token Valid:** If all checks pass, the token is considered valid. ## 5+ Practical Scenarios and Their JWT Verification Implications Understanding the theoretical underpinnings is essential, but seeing how JWT verification plays out in real-world scenarios solidifies its importance. ### Scenario 1: Stateless Authentication with a Single-Page Application (SPA) * **Description:** A user logs into an SPA. Upon successful authentication, the server issues a JWT. The SPA stores this JWT (e.g., in `localStorage` or `sessionStorage`) and includes it in the `Authorization` header (typically as `Bearer `) for all subsequent API requests. * **Verification Implication:** The backend API server acts as the JWT decoder. It receives the token, extracts the signature, decodes the header to identify the signing algorithm (e.g., HS256). It then uses a pre-shared secret key (configured securely on the server) to verify the signature against the header and payload. If valid, the payload claims (like user ID, roles) are used to authorize the request without needing to query a database for user session information. The `exp` claim is crucial here to invalidate old sessions. ### Scenario 2: Microservices Communication with JWT Propagation * **Description:** A user authenticates with an API Gateway. The gateway issues a JWT. When an internal service needs to call another service, it forwards the original JWT. * **Verification Implication:** Each microservice that needs to authorize a request based on the JWT acts as a decoder. If all microservices trust the same signing key (either a shared secret or a public key for asymmetric signing), they can independently verify the JWT. This avoids tight coupling and the need for a central session store. The `aud` claim can be used here to ensure a token issued for one service isn't used to access another. ### Scenario 3: Securely Transmitting User Permissions * **Description:** A user is granted specific permissions within an application. This information is embedded in a JWT issued after login. * **Verification Implication:** When the user performs an action, the JWT is decoded. The decoder verifies the signature and then checks the custom claims within the payload that represent permissions. For example, a claim like `"permissions": ["read:users", "write:products"]` would be checked against the requested action. The integrity provided by the signature ensures these permissions haven't been tampered with. ### Scenario 4: Third-Party API Integration with Asymmetric Cryptography * **Description:** A third-party service needs to call your API. They generate a public/private key pair and provide you with their public key. They then sign JWTs with their private key and send them to your API. * **Verification Implication:** Your API server acts as the JWT decoder. It receives the token, decodes the header to find the `alg` (e.g., RS256). It then uses the *provided public key* of the third party to verify the signature. This is an asymmetric verification. If the signature matches, you trust that the token originated from the third party and hasn't been altered. ### Scenario 5: Token Revocation Strategies (and their limitations with JWTs) * **Description:** While JWTs are designed to be stateless, there are scenarios where immediate revocation is necessary (e.g., a user's account is compromised, and their active sessions need to be terminated instantly). * **Verification Implication:** Standard JWT verification (signature and expiration) does *not* inherently support immediate revocation. If the token is still valid according to its `exp` claim, a decoder will accept it. To implement revocation, additional mechanisms are required: * **Token Blacklisting:** Maintain a server-side list of revoked tokens (e.g., by `jti` - JWT ID). The decoder would check this blacklist *after* verifying the signature and claims. This reintroduces state. * **Short Expiration Times:** Issue JWTs with very short expiration times and rely on refresh tokens for longer sessions. When a token needs to be revoked, the corresponding refresh token can be invalidated. * **Custom Claims:** Embed a "revocation timestamp" in the JWT. The decoder checks if the `iat` or `exp` is before this revocation timestamp. ### Scenario 6: OAuth 2.0 Authorization Server Token Validation * **Description:** In an OAuth 2.0 flow, an authorization server issues access tokens (often JWTs) to a client application. The client application then uses this access token to access a resource server. * **Verification Implication:** The resource server receives the JWT access token. It must verify the token's signature using the public key of the authorization server (usually published at a JWKS endpoint). It also validates claims like `iss` (to ensure it's from the correct authorization server), `aud` (to ensure it's intended for this resource server), and `exp`. This ensures that the client is authorized to access the protected resources. ## Global Industry Standards and Best Practices The security and interoperability of JWTs are underpinned by a suite of RFCs (Request for Comments) and community-driven best practices. Adhering to these standards is paramount for building secure and robust systems. ### Key RFCs: * **RFC 7515: JSON Web Signature (JWS):** Defines the structure of a JWS, which is the format of a JWT with a signature. It specifies how to represent signed JSON objects. * **RFC 7516: JSON Web Encryption (JWE):** Defines the structure for encrypting JSON objects. While JWTs often focus on signing for integrity and authentication, JWE can be used to encrypt the payload for confidentiality. * **RFC 7517: JSON Web Key (JWK):** Specifies a JSON-based format for representing cryptographic keys. This is crucial for sharing public keys used in asymmetric signing. * **RFC 7518: JSON Web Algorithms (JWA):** Defines the cryptographic algorithms that can be used with JWS and JWE, such as HMAC, RSA, and ECDSA. * **RFC 7519: JSON Web Token (JWT):** The foundational RFC that defines the JWT structure, claims, and the overall concept of a token. * **RFC 7636: Proof Key for Code Exchange by OAuth Clients (PKCE):** While not directly a JWT RFC, PKCE is a crucial security extension for OAuth 2.0 authorization code flows that often involve JWTs, preventing authorization code interception attacks. ### Best Practices for JWT Verification: 1. **Always Verify the Signature:** Never trust a JWT without verifying its signature. This is the most fundamental security principle. 2. **Use Strong, Supported Algorithms:** Prefer modern and secure algorithms like RS256, ES256, or HS256. Avoid outdated or insecure algorithms. 3. **Reject `alg: "none"`:** Explicitly disallow the `alg: "none"` header in your decoder. This is a common attack vector. 4. **Validate `exp` Claim:** Always check the expiration time to prevent the use of stale tokens. 5. **Validate `aud` Claim:** Ensure the token is intended for your application or service. 6. **Validate `iss` Claim:** Verify the issuer of the token to ensure it comes from a trusted authority. 7. **Securely Store and Manage Keys:** * **Symmetric Keys:** Treat them as highly sensitive secrets. Use environment variables, secret management systems (like HashiCorp Vault, AWS Secrets Manager), or secure configuration files. Do not hardcode them. * **Asymmetric Public Keys:** Obtain them from trusted sources. Regularly rotate them if possible. For JWKS, ensure the endpoint is secured (HTTPS) and consider caching strategies with appropriate expiry. 8. **Use JWT IDs (`jti`) for Uniqueness:** Assign a unique identifier to each JWT. This can be useful for implementing revocation lists. 9. **Consider Token Revocation Strategies:** If immediate revocation is a requirement, implement a mechanism (like blacklisting) in conjunction with JWTs, understanding the trade-offs with statelessness. 10. **Validate Claims Based on Your Application's Needs:** Implement checks for custom claims as required by your business logic. 11. **Avoid Sensitive Data in the Payload:** While the signature ensures integrity, the payload is only Base64Url encoded by default. For confidential data, consider JWT encryption (JWE) or use JWTs solely for authentication/authorization and fetch sensitive user data from a secure backend service using the authenticated user's ID from the JWT. 12. **Keep Libraries Updated:** Ensure your JWT decoding libraries are up-to-date to benefit from security patches and performance improvements. ## Multi-language Code Vault: JWT Verification Examples Here, we provide practical code snippets demonstrating JWT verification using popular libraries in different programming languages. These examples showcase how the `jwt-decoder` concept is implemented across various ecosystems. ### JavaScript (Node.js) - `jsonwebtoken` library javascript // Install: npm install jsonwebtoken const jwt = require('jsonwebtoken'); // --- Symmetric Key Example (HS256) --- const secretKey = 'your-super-secret-key-that-should-be-long-and-random'; // NEVER hardcode this in production! const tokenSymmetric = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKK92w7wK3dp8Gj8YvjF71Nl9yF70qA-8'; // Example token async function verifySymmetricToken(token, secret) { try { const decoded = await jwt.verify(token, secret, { algorithms: ['HS256'] }); console.log('Symmetric Token Verified:', decoded); // You can now use 'decoded' which contains the payload return decoded; } catch (err) { console.error('Symmetric Token Verification Failed:', err.message); throw err; // Re-throw to handle appropriately } } // --- Asymmetric Key Example (RS256) --- // Assume you have your public key in PEM format const publicKey = `-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEe0mJ5S9o6g1B9D+p9+11KqR+4Y5n u8tE6/p/M0zF/k8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8F/m8