@@ -181,17 +181,21 @@ open class OAuth2Base: OAuth2Securable {
181181
182182 - verbose (Bool, false by default, applies to client logging)
183183 */
184- override public init ( settings: OAuth2JSON ) {
185- clientConfig = OAuth2ClientConfig ( settings: settings)
184+ override public init ( settings: OAuth2JSON , serverMetadata : OAuth2ServerMetadata ? ) {
185+ clientConfig = OAuth2ClientConfig ( settings: settings, serverMetadata : serverMetadata )
186186
187187 // auth configuration options
188188 if let ttl = settings [ " title " ] as? String {
189189 authConfig. ui. title = ttl
190190 }
191- super. init ( settings: settings)
191+
192+ super. init ( settings: settings, serverMetadata: serverMetadata)
193+
194+ if let serverMetadata {
195+ self . validateConfiguration ( against: serverMetadata)
196+ }
192197 }
193198
194-
195199 // MARK: - Keychain Integration
196200
197201 /** Overrides base implementation to return the authorize URL. */
@@ -290,28 +294,39 @@ open class OAuth2Base: OAuth2Securable {
290294 /**
291295 Internally used on error, calls the callbacks on the main thread with the appropriate error message.
292296
293- This method is only made public in case you want to create a subclass and need to call `didFail(error:)` at an override point. If you
294- call this method yourself on your OAuth2 instance you might screw things up royally.
297+ This method handles authorization failures by cleaning up state, logging the error,
298+ and notifying all registered callbacks. It ensures proper cleanup of async continuations
299+ and maintains thread safety by dispatching callbacks to the main thread.
300+
301+ This method is only made public in case you want to create a subclass and need to call
302+ `didFail(with:)` at an override point. If you call this method yourself on your OAuth2
303+ instance you might cause unexpected behavior.
295304
296- - parameter error: The error that led to authorization failure; will use `.requestCancelled` on the callbacks if nil is passed
305+ - parameter error: The error that led to authorization failure; will use `.requestCancelled` if nil is passed
297306 */
298307 public final func didFail( with error: OAuth2Error ? ) {
299- var finalError = error
300- if let error = finalError {
301- logger? . debug ( " OAuth2 " , msg: " \( error) " )
302- }
303- else {
304- finalError = OAuth2Error . requestCancelled
308+ let finalError = error ?? OAuth2Error . requestCancelled
309+
310+ // Log the error with appropriate level
311+ if let error = error {
312+ logger? . warn ( " OAuth2 " , msg: " Authorization failed with error: \( error) " )
313+ } else {
314+ logger? . debug ( " OAuth2 " , msg: " Authorization was cancelled or aborted " )
305315 }
316+
317+ // Dispatch callbacks to main thread with proper error handling
306318 callOnMainThread ( ) {
307319 self . isAuthorizing = false
308320 self . internalAfterAuthorizeOrFail ? ( true , finalError)
309321 self . afterAuthorizeOrFail ? ( nil , finalError)
310322 }
311323
312- // Finish `doAuthorize` call
313- self . doAuthorizeContinuation? . resume ( throwing: error ?? OAuth2Error . requestCancelled)
314- self . doAuthorizeContinuation = nil
324+ // Complete async continuation if present
325+ if let continuation = doAuthorizeContinuation {
326+ continuation. resume ( throwing: finalError)
327+ doAuthorizeContinuation = nil
328+ logger? . trace ( " OAuth2 " , msg: " Completed async authorization continuation with error " )
329+ }
315330 }
316331
317332 /**
@@ -504,87 +519,27 @@ open class OAuth2Base: OAuth2Securable {
504519 open func assureRefreshTokenParamsAreValid( _ params: OAuth2JSON ) throws {
505520 }
506521
507- }
508-
509-
510- /**
511- Class, internally used, to store current authorization context, such as state and redirect-url.
512- */
513- open class OAuth2ContextStore {
514-
515- /// Currently used redirect_url.
516- open var redirectURL : String ?
517-
518- /// Current code verifier used for PKCE
519- public internal( set) var codeVerifier : String ?
520- public let codeChallengeMethod = " S256 "
521-
522- /// The current state.
523- internal var _state = " "
524-
525522 /**
526- The state sent to the server when requesting a token .
523+ Validates the current OAuth2 configuration against server metadata .
527524
528- We internally generate a UUID and use the first 8 chars if `_state` is empty.
529- */
530- open var state : String {
531- if _state. isEmpty {
532- _state = UUID ( ) . uuidString
533- _state = String ( _state [ _state. startIndex..< _state. index ( _state. startIndex, offsetBy: 8 ) ] ) // only use the first 8 chars, should be enough
534- }
535- return _state
536- }
525+ This method checks if the configured grant type and response type are supported
526+ by the authorization server according to the provided metadata.
537527
538- /**
539- Checks that given state matches the internal state.
540-
541- - parameter state: The state to check (may be nil)
542- - returns: true if state matches, false otherwise or if given state is nil.
528+ - parameter serverMetadata: The server metadata to validate against
543529 */
544- func matchesState( _ state: String ? ) -> Bool {
545- if let st = state {
546- return st == _state
530+ open func validateConfiguration( against serverMetadata: OAuth2ServerMetadata ) {
531+ // validate the grant type
532+ let grantType = type ( of: self ) . grantType
533+ let grantTypesSupported = serverMetadata. grantTypesSupported ?? [ OAuth2GrantTypes . authorizationCode, OAuth2GrantTypes . implicit]
534+ if !grantTypesSupported. contains ( grantType) {
535+ logger? . warn ( " OAuth2 " , msg: " The authorization server doesn't support the \" \( grantType) \" grant type. " )
547536 }
548- return false
549- }
550-
551- /**
552- Resets current state so it gets regenerated next time it's needed.
553- */
554- func resetState( ) {
555- _state = " "
556- }
557-
558- // MARK: - PKCE
559-
560- /**
561- Generates a new code verifier string
562- */
563- open func generateCodeVerifier( ) {
564- var buffer = [ UInt8] ( repeating: 0 , count: 32 )
565- _ = SecRandomCopyBytes ( kSecRandomDefault, buffer. count, & buffer)
566- codeVerifier = Data ( buffer) . base64EncodedString ( )
567- . replacingOccurrences ( of: " + " , with: " - " )
568- . replacingOccurrences ( of: " / " , with: " _ " )
569- . replacingOccurrences ( of: " = " , with: " " )
570- . trimmingCharacters ( in: . whitespaces)
571- }
572-
573-
574- open func codeChallenge( ) -> String ? {
575- guard let verifier = codeVerifier, let data = verifier. data ( using: . utf8) else { return nil }
576- var buffer = [ UInt8] ( repeating: 0 , count: Int ( CC_SHA256_DIGEST_LENGTH) )
577- data. withUnsafeBytes {
578- _ = CC_SHA256 ( $0. baseAddress, CC_LONG ( data. count) , & buffer)
537+
538+ // validate the response type
539+ if let responseType = type ( of: self ) . responseType,
540+ !serverMetadata. responseTypesSupported. contains ( responseType) {
541+ logger? . warn ( " OAuth2 " , msg: " The authorization server doesn't support the \" \( responseType) \" response type. " )
579542 }
580- let hash = Data ( buffer)
581- let challenge = hash. base64EncodedString ( )
582- . replacingOccurrences ( of: " + " , with: " - " )
583- . replacingOccurrences ( of: " / " , with: " _ " )
584- . replacingOccurrences ( of: " = " , with: " " )
585- . trimmingCharacters ( in: . whitespaces)
586- return challenge
587543 }
588544
589545}
590-
0 commit comments