// // File.swift // Gulliver // // Created by Bailey Townsend on 1/20/26. // import Foundation /// OAuth 2.0 Client Metadata /// Based on RFC 7591 Section 2 and related specifications struct OAuthClientMetadata: Codable { /// REQUIRED. Array of redirection URIs for use in redirect-based flows let redirectUris: [URL] /// OPTIONAL. Array of OAuth 2.0 response_type values that the client will restrict itself to using let responseTypes: [String]? /// OPTIONAL. Array of OAuth 2.0 grant types that the client will restrict itself to using let grantTypes: [String]? /// OPTIONAL. String containing a space-separated list of scope values let scope: String? /// OPTIONAL. Indicator of the requested authentication method for the token endpoint let tokenEndpointAuthMethod: String? /// OPTIONAL. JWS algorithm that must be used for signing request objects let tokenEndpointAuthSigningAlg: String? /// OPTIONAL. JWS algorithm required for signing UserInfo Responses let userinfoSignedResponseAlg: String? /// OPTIONAL. JWE algorithm required for encrypting UserInfo Responses let userinfoEncryptedResponseAlg: String? /// OPTIONAL. URL string referencing the client's JSON Web Key (JWK) Set document let jwksUri: URL? /// OPTIONAL. Client's JSON Web Key Set document value let jwks: [String: Any]? /// OPTIONAL. Kind of application: "web" or "native" let applicationType: String? /// OPTIONAL. Subject type requested for responses to this client: "public" or "pairwise" let subjectType: String? /// OPTIONAL. JWS algorithm that must be used for signing Request Objects let requestObjectSigningAlg: String? /// OPTIONAL. JWS algorithm required for signing the ID Token issued to this client let idTokenSignedResponseAlg: String? /// OPTIONAL. JWS algorithm required for signing authorization responses let authorizationSignedResponseAlg: String? /// OPTIONAL. JWE encryption encoding for authorization responses let authorizationEncryptedResponseEnc: String? /// OPTIONAL. JWE algorithm required for encrypting authorization responses let authorizationEncryptedResponseAlg: String? /// OPTIONAL. Unique client identifier let clientId: String? /// OPTIONAL. Human-readable name of the client let clientName: String? /// OPTIONAL. URL of the home page of the client let clientUri: URL? /// OPTIONAL. URL that the client provides to the end-user to read about how the profile data will be used let policyUri: URL? /// OPTIONAL. URL that the client provides to the end-user to read about the client's terms of service let tosUri: URL? /// OPTIONAL. URL that references a logo for the client application let logoUri: URL? /// OPTIONAL. Default Maximum Authentication Age in seconds /// Specifies that the End-User MUST be actively authenticated if the End-User was authenticated /// longer ago than the specified number of seconds let defaultMaxAge: Int? /// OPTIONAL. Whether the auth_time Claim in the ID Token is REQUIRED let requireAuthTime: Bool? /// OPTIONAL. Array of email addresses of people responsible for this client let contacts: [String]? /// OPTIONAL. Whether TLS client certificate bound access tokens are requested let tlsClientCertificateBoundAccessTokens: Bool? /// OPTIONAL. Whether DPoP-bound access tokens are requested (RFC 9449 Section 5.2) let dpopBoundAccessTokens: Bool? /// OPTIONAL. Array of authorization details types supported (RFC 9396 Section 14.5) let authorizationDetailsTypes: [String]? enum CodingKeys: String, CodingKey { case redirectUris = "redirect_uris" case responseTypes = "response_types" case grantTypes = "grant_types" case scope case tokenEndpointAuthMethod = "token_endpoint_auth_method" case tokenEndpointAuthSigningAlg = "token_endpoint_auth_signing_alg" case userinfoSignedResponseAlg = "userinfo_signed_response_alg" case userinfoEncryptedResponseAlg = "userinfo_encrypted_response_alg" case jwksUri = "jwks_uri" case jwks case applicationType = "application_type" case subjectType = "subject_type" case requestObjectSigningAlg = "request_object_signing_alg" case idTokenSignedResponseAlg = "id_token_signed_response_alg" case authorizationSignedResponseAlg = "authorization_signed_response_alg" case authorizationEncryptedResponseEnc = "authorization_encrypted_response_enc" case authorizationEncryptedResponseAlg = "authorization_encrypted_response_alg" case clientId = "client_id" case clientName = "client_name" case clientUri = "client_uri" case policyUri = "policy_uri" case tosUri = "tos_uri" case logoUri = "logo_uri" case defaultMaxAge = "default_max_age" case requireAuthTime = "require_auth_time" case contacts case tlsClientCertificateBoundAccessTokens = "tls_client_certificate_bound_access_tokens" case dpopBoundAccessTokens = "dpop_bound_access_tokens" case authorizationDetailsTypes = "authorization_details_types" } // Custom decoder to handle the jwks field which can contain arbitrary JSON init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) redirectUris = try container.decode([URL].self, forKey: .redirectUris) responseTypes = try container.decodeIfPresent([String].self, forKey: .responseTypes) grantTypes = try container.decodeIfPresent([String].self, forKey: .grantTypes) scope = try container.decodeIfPresent(String.self, forKey: .scope) tokenEndpointAuthMethod = try container.decodeIfPresent(String.self, forKey: .tokenEndpointAuthMethod) tokenEndpointAuthSigningAlg = try container.decodeIfPresent(String.self, forKey: .tokenEndpointAuthSigningAlg) userinfoSignedResponseAlg = try container.decodeIfPresent(String.self, forKey: .userinfoSignedResponseAlg) userinfoEncryptedResponseAlg = try container.decodeIfPresent(String.self, forKey: .userinfoEncryptedResponseAlg) jwksUri = try container.decodeIfPresent(URL.self, forKey: .jwksUri) // Decode jwks as generic dictionary if let jwksData = try? container.decodeIfPresent(Data.self, forKey: .jwks), let jwksDict = try? JSONSerialization.jsonObject(with: jwksData) as? [String: Any] { jwks = jwksDict } else { jwks = nil } applicationType = try container.decodeIfPresent(String.self, forKey: .applicationType) subjectType = try container.decodeIfPresent(String.self, forKey: .subjectType) requestObjectSigningAlg = try container.decodeIfPresent(String.self, forKey: .requestObjectSigningAlg) idTokenSignedResponseAlg = try container.decodeIfPresent(String.self, forKey: .idTokenSignedResponseAlg) authorizationSignedResponseAlg = try container.decodeIfPresent(String.self, forKey: .authorizationSignedResponseAlg) authorizationEncryptedResponseEnc = try container.decodeIfPresent(String.self, forKey: .authorizationEncryptedResponseEnc) authorizationEncryptedResponseAlg = try container.decodeIfPresent(String.self, forKey: .authorizationEncryptedResponseAlg) clientId = try container.decodeIfPresent(String.self, forKey: .clientId) clientName = try container.decodeIfPresent(String.self, forKey: .clientName) clientUri = try container.decodeIfPresent(URL.self, forKey: .clientUri) policyUri = try container.decodeIfPresent(URL.self, forKey: .policyUri) tosUri = try container.decodeIfPresent(URL.self, forKey: .tosUri) logoUri = try container.decodeIfPresent(URL.self, forKey: .logoUri) defaultMaxAge = try container.decodeIfPresent(Int.self, forKey: .defaultMaxAge) requireAuthTime = try container.decodeIfPresent(Bool.self, forKey: .requireAuthTime) contacts = try container.decodeIfPresent([String].self, forKey: .contacts) tlsClientCertificateBoundAccessTokens = try container.decodeIfPresent(Bool.self, forKey: .tlsClientCertificateBoundAccessTokens) dpopBoundAccessTokens = try container.decodeIfPresent(Bool.self, forKey: .dpopBoundAccessTokens) authorizationDetailsTypes = try container.decodeIfPresent([String].self, forKey: .authorizationDetailsTypes) } // Custom encoder to handle the jwks field func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(redirectUris, forKey: .redirectUris) try container.encodeIfPresent(responseTypes, forKey: .responseTypes) try container.encodeIfPresent(grantTypes, forKey: .grantTypes) try container.encodeIfPresent(scope, forKey: .scope) try container.encodeIfPresent(tokenEndpointAuthMethod, forKey: .tokenEndpointAuthMethod) try container.encodeIfPresent(tokenEndpointAuthSigningAlg, forKey: .tokenEndpointAuthSigningAlg) try container.encodeIfPresent(userinfoSignedResponseAlg, forKey: .userinfoSignedResponseAlg) try container.encodeIfPresent(userinfoEncryptedResponseAlg, forKey: .userinfoEncryptedResponseAlg) try container.encodeIfPresent(jwksUri, forKey: .jwksUri) // Encode jwks as generic dictionary if let jwks = jwks, let jwksData = try? JSONSerialization.data(withJSONObject: jwks) { try container.encode(jwksData, forKey: .jwks) } try container.encodeIfPresent(applicationType, forKey: .applicationType) try container.encodeIfPresent(subjectType, forKey: .subjectType) try container.encodeIfPresent(requestObjectSigningAlg, forKey: .requestObjectSigningAlg) try container.encodeIfPresent(idTokenSignedResponseAlg, forKey: .idTokenSignedResponseAlg) try container.encodeIfPresent(authorizationSignedResponseAlg, forKey: .authorizationSignedResponseAlg) try container.encodeIfPresent(authorizationEncryptedResponseEnc, forKey: .authorizationEncryptedResponseEnc) try container.encodeIfPresent(authorizationEncryptedResponseAlg, forKey: .authorizationEncryptedResponseAlg) try container.encodeIfPresent(clientId, forKey: .clientId) try container.encodeIfPresent(clientName, forKey: .clientName) try container.encodeIfPresent(clientUri, forKey: .clientUri) try container.encodeIfPresent(policyUri, forKey: .policyUri) try container.encodeIfPresent(tosUri, forKey: .tosUri) try container.encodeIfPresent(logoUri, forKey: .logoUri) try container.encodeIfPresent(defaultMaxAge, forKey: .defaultMaxAge) try container.encodeIfPresent(requireAuthTime, forKey: .requireAuthTime) try container.encodeIfPresent(contacts, forKey: .contacts) try container.encodeIfPresent(tlsClientCertificateBoundAccessTokens, forKey: .tlsClientCertificateBoundAccessTokens) try container.encodeIfPresent(dpopBoundAccessTokens, forKey: .dpopBoundAccessTokens) try container.encodeIfPresent(authorizationDetailsTypes, forKey: .authorizationDetailsTypes) } } /// OAuth 2.0 Authorization Server Metadata /// Based on RFC 8414 and related specifications struct OAuthAuthorizationServerMetadata: Codable { /// The authorization server's issuer identifier let issuer: String /// Array of claim types supported let claimsSupported: [String]? /// Languages and scripts supported for claims let claimsLocalesSupported: [String]? /// Whether the claims parameter is supported let claimsParameterSupported: Bool? /// Whether the request parameter is supported let requestParameterSupported: Bool? /// Whether the request_uri parameter is supported let requestUriParameterSupported: Bool? /// Whether request_uri values must be pre-registered let requireRequestUriRegistration: Bool? /// Array of OAuth 2.0 scope values supported let scopesSupported: [String]? /// Subject identifier types supported let subjectTypesSupported: [String]? /// Response types supported let responseTypesSupported: [String]? /// Response modes supported let responseModesSupported: [String]? /// Grant types supported let grantTypesSupported: [String]? /// PKCE code challenge methods supported let codeChallengeMethodsSupported: [String]? /// Languages and scripts supported for UI let uiLocalesSupported: [String]? /// Algorithms supported for signing ID tokens let idTokenSigningAlgValuesSupported: [String]? /// Display values supported let displayValuesSupported: [String]? /// Prompt values supported let promptValuesSupported: [String]? /// Algorithms supported for signing request objects let requestObjectSigningAlgValuesSupported: [String]? /// Whether authorization response issuer parameter is supported let authorizationResponseIssParameterSupported: Bool? /// Authorization details types supported let authorizationDetailsTypesSupported: [String]? /// Algorithms supported for encrypting request objects let requestObjectEncryptionAlgValuesSupported: [String]? /// Encryption encodings supported for request objects let requestObjectEncryptionEncValuesSupported: [String]? /// URL of the authorization server's JWK Set document let jwksUri: URL? /// URL of the authorization endpoint let authorizationEndpoint: URL /// URL of the token endpoint let tokenEndpoint: URL /// Authentication methods supported at token endpoint (RFC 8414 Section 2) let tokenEndpointAuthMethodsSupported: [String]? /// Signing algorithms supported for token endpoint authentication let tokenEndpointAuthSigningAlgValuesSupported: [String]? /// URL of the revocation endpoint let revocationEndpoint: URL? /// Authentication methods supported at revocation endpoint let revocationEndpointAuthMethodsSupported: [String]? /// Signing algorithms supported for revocation endpoint authentication let revocationEndpointAuthSigningAlgValuesSupported: [String]? /// URL of the introspection endpoint let introspectionEndpoint: URL? /// Authentication methods supported at introspection endpoint let introspectionEndpointAuthMethodsSupported: [String]? /// Signing algorithms supported for introspection endpoint authentication let introspectionEndpointAuthSigningAlgValuesSupported: [String]? /// URL of the pushed authorization request endpoint let pushedAuthorizationRequestEndpoint: URL? /// Authentication methods supported at PAR endpoint let pushedAuthorizationRequestEndpointAuthMethodsSupported: [String]? /// Signing algorithms supported for PAR endpoint authentication let pushedAuthorizationRequestEndpointAuthSigningAlgValuesSupported: [String]? /// Whether pushed authorization requests are required let requirePushedAuthorizationRequests: Bool? /// URL of the UserInfo endpoint let userinfoEndpoint: URL? /// URL of the end session endpoint let endSessionEndpoint: URL? /// URL of the dynamic client registration endpoint let registrationEndpoint: URL? /// DPoP signing algorithms supported (RFC 9449 Section 5.1) let dpopSigningAlgValuesSupported: [String]? /// Protected resource URIs (RFC 9728 Section 4) let protectedResources: [URL]? /// Whether client ID metadata document is supported let clientIdMetadataDocumentSupported: Bool? enum CodingKeys: String, CodingKey { case issuer case claimsSupported = "claims_supported" case claimsLocalesSupported = "claims_locales_supported" case claimsParameterSupported = "claims_parameter_supported" case requestParameterSupported = "request_parameter_supported" case requestUriParameterSupported = "request_uri_parameter_supported" case requireRequestUriRegistration = "require_request_uri_registration" case scopesSupported = "scopes_supported" case subjectTypesSupported = "subject_types_supported" case responseTypesSupported = "response_types_supported" case responseModesSupported = "response_modes_supported" case grantTypesSupported = "grant_types_supported" case codeChallengeMethodsSupported = "code_challenge_methods_supported" case uiLocalesSupported = "ui_locales_supported" case idTokenSigningAlgValuesSupported = "id_token_signing_alg_values_supported" case displayValuesSupported = "display_values_supported" case promptValuesSupported = "prompt_values_supported" case requestObjectSigningAlgValuesSupported = "request_object_signing_alg_values_supported" case authorizationResponseIssParameterSupported = "authorization_response_iss_parameter_supported" case authorizationDetailsTypesSupported = "authorization_details_types_supported" case requestObjectEncryptionAlgValuesSupported = "request_object_encryption_alg_values_supported" case requestObjectEncryptionEncValuesSupported = "request_object_encryption_enc_values_supported" case jwksUri = "jwks_uri" case authorizationEndpoint = "authorization_endpoint" case tokenEndpoint = "token_endpoint" case tokenEndpointAuthMethodsSupported = "token_endpoint_auth_methods_supported" case tokenEndpointAuthSigningAlgValuesSupported = "token_endpoint_auth_signing_alg_values_supported" case revocationEndpoint = "revocation_endpoint" case revocationEndpointAuthMethodsSupported = "revocation_endpoint_auth_methods_supported" case revocationEndpointAuthSigningAlgValuesSupported = "revocation_endpoint_auth_signing_alg_values_supported" case introspectionEndpoint = "introspection_endpoint" case introspectionEndpointAuthMethodsSupported = "introspection_endpoint_auth_methods_supported" case introspectionEndpointAuthSigningAlgValuesSupported = "introspection_endpoint_auth_signing_alg_values_supported" case pushedAuthorizationRequestEndpoint = "pushed_authorization_request_endpoint" case pushedAuthorizationRequestEndpointAuthMethodsSupported = "pushed_authorization_request_endpoint_auth_methods_supported" case pushedAuthorizationRequestEndpointAuthSigningAlgValuesSupported = "pushed_authorization_request_endpoint_auth_signing_alg_values_supported" case requirePushedAuthorizationRequests = "require_pushed_authorization_requests" case userinfoEndpoint = "userinfo_endpoint" case endSessionEndpoint = "end_session_endpoint" case registrationEndpoint = "registration_endpoint" case dpopSigningAlgValuesSupported = "dpop_signing_alg_values_supported" case protectedResources = "protected_resources" case clientIdMetadataDocumentSupported = "client_id_metadata_document_supported" } } /// OAuth 2.0 Protected Resource Metadata /// Based on RFC 9728 Section 3.2 /// - SeeAlso: [RFC 9728 Section 3.2](https://www.rfc-editor.org/rfc/rfc9728.html#section-3.2) struct OAuthProtectedResourceMetadata: Codable { /// REQUIRED. The protected resource's resource identifier, which is a URL that /// uses the https scheme and has no query or fragment components. let resource: URL /// OPTIONAL. JSON array containing a list of OAuth authorization server issuer /// identifiers, as defined in RFC8414, for authorization servers that can be /// used with this protected resource. let authorizationServers: [String]? /// OPTIONAL. URL of the protected resource's JWK Set document. let jwksUri: URL? /// RECOMMENDED. JSON array containing a list of the OAuth 2.0 scope values that /// are used in authorization requests to request access to this protected resource. let scopesSupported: [String]? /// OPTIONAL. JSON array containing a list of the supported methods of sending /// an OAuth 2.0 Bearer Token to the protected resource. let bearerMethodsSupported: [String]? /// OPTIONAL. JSON array containing a list of the JWS signing algorithms /// supported by the protected resource for signing resource responses. let resourceSigningAlgValuesSupported: [String]? /// OPTIONAL. URL of a page containing human-readable information that /// developers might want or need to know when using the protected resource. let resourceDocumentation: URL? /// OPTIONAL. URL that the protected resource provides to read about the /// protected resource's requirements on how the client can use the data. let resourcePolicyUri: URL? /// OPTIONAL. URL that the protected resource provides to read about the /// protected resource's terms of service. let resourceTosUri: URL? enum CodingKeys: String, CodingKey { case resource case authorizationServers = "authorization_servers" case jwksUri = "jwks_uri" case scopesSupported = "scopes_supported" case bearerMethodsSupported = "bearer_methods_supported" case resourceSigningAlgValuesSupported = "resource_signing_alg_values_supported" case resourceDocumentation = "resource_documentation" case resourcePolicyUri = "resource_policy_uri" case resourceTosUri = "resource_tos_uri" } } /// OAuth 2.0 Pushed Authorization Request (PAR) Response /// Based on RFC 9126 Section 2.2 /// - SeeAlso: [RFC 9126 Section 2.2](https://www.rfc-editor.org/rfc/rfc9126.html#section-2.2) struct OAuthPushedAuthorizationResponse: Codable { /// REQUIRED. The request URI corresponding to the authorization request posted. /// This URI is a single-use reference to the respective request data in the /// subsequent authorization request. The way the authorization process obtains /// the authorization request data is at the discretion of the authorization server /// and is out of scope of this specification. let requestUri: String /// REQUIRED. A JSON number that represents the lifetime of the request URI in seconds. /// The request URI lifetime is at the discretion of the authorization server but /// will typically be relatively short (e.g., between 5 and 600 seconds). let expiresIn: Int enum CodingKeys: String, CodingKey { case requestUri = "request_uri" case expiresIn = "expires_in" } }