-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extend Token with HasClaims
for custom claims
#204
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
// | ||
// Copyright (c) 2024-Present, Okta, Inc. and/or its affiliates. All rights reserved. | ||
// The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.") | ||
// | ||
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// | ||
// See the License for the specific language governing permissions and limitations under the License. | ||
// | ||
|
||
import Foundation | ||
|
||
/// Extension which introduces single-value conversion functions for ``ClaimConvertable`` types. | ||
public extension HasClaims { | ||
/// Retrieve the value using a payload key, converting to the requested ``ClaimConvertable`` type. | ||
/// - Parameters: | ||
/// - key: String payload key name. | ||
/// - Returns: Value converted to the requested type. | ||
func value<T: ClaimConvertable>(for key: String) throws -> T { | ||
guard let value = T.convert(from: payload[key]) | ||
else { | ||
throw ClaimError.missingRequiredValue(key: key) | ||
} | ||
return value | ||
} | ||
|
||
/// Retrieve the value using a claim enum, converting to the requested ``ClaimConvertable`` type. | ||
/// - Parameters: | ||
/// - claim: Claim enum value. | ||
/// - Returns: Value converted to the requested type. | ||
func value<T: ClaimConvertable>(for claim: ClaimType) throws -> T { | ||
try value(for: claim.rawValue) | ||
} | ||
|
||
/// Retrieve the optional value using a payload key, converting to the requested ``ClaimConvertable`` type. | ||
/// - Parameters: | ||
/// - key: String payload key name. | ||
/// - Returns: Optional value converted to the requested type. | ||
func value<T: ClaimConvertable>(for key: String) -> T? { | ||
T.convert(from: payload[key]) | ||
} | ||
|
||
/// Retrieve the optional value using a claim enum, converting to the requested ``ClaimConvertable`` type. | ||
/// - Parameters: | ||
/// - claim: Claim enum value. | ||
/// - Returns: Optional value converted to the requested type. | ||
func value<T: ClaimConvertable>(for claim: ClaimType) -> T? { | ||
value(for: claim.rawValue) | ||
} | ||
} | ||
|
||
/// Extension which introduces array-value conversion functions for ``ClaimConvertable`` types. | ||
public extension HasClaims { | ||
/// Returns the value for the given key as an array of values converted using a``ClaimConvertable`` type. | ||
/// - Parameter key: String payload key name. | ||
/// - Returns: Value converted to an array of the requested type. | ||
func value<T: ClaimConvertable>(for key: String) throws -> [T] { | ||
guard let array = payload[key] as? [ClaimConvertable] | ||
else { | ||
throw ClaimError.missingRequiredValue(key: key) | ||
} | ||
|
||
return array.compactMap { T.convert(from: $0) } | ||
} | ||
|
||
/// Returns the value for the given key as an array of values converted using a``ClaimConvertable`` type. | ||
/// - Parameter key: String payload key name. | ||
/// - Returns: Value converted to an array of the requested type. | ||
func value<T: ClaimConvertable>(for claim: ClaimType) throws -> [T] { | ||
try value(for: claim.rawValue) | ||
} | ||
|
||
/// Returns the optional value for the given key as an array of values converted using a``ClaimConvertable`` type. | ||
/// - Parameter key: String payload key name. | ||
/// - Returns: Optional value converted to an array of the requested type. | ||
func value<T: ClaimConvertable>(for key: String) -> [T]? { | ||
let array = payload[key] as? [ClaimConvertable] | ||
return array?.compactMap { T.convert(from: $0) } | ||
} | ||
|
||
/// Returns the optional value for the given key as an array of values converted using a``ClaimConvertable`` type. | ||
/// - Parameter key: String payload key name. | ||
/// - Returns: Optional value converted to an array of the requested type. | ||
func value<T: ClaimConvertable>(for claim: ClaimType) -> [T]? { | ||
value(for: claim.rawValue) | ||
} | ||
} | ||
|
||
/// Extension which introduces dictionary-value conversion functions for ``ClaimConvertable`` types. | ||
public extension HasClaims { | ||
/// Returns the value for the given key as an array of values converted using a``ClaimConvertable`` type. | ||
/// - Parameter key: String payload key name. | ||
/// - Returns: Value converted to an array of the requested type. | ||
func value<T: ClaimConvertable>(for key: String) throws -> [String: T] { | ||
guard let dictionary = payload[key] as? [String: ClaimConvertable] | ||
else { | ||
throw ClaimError.missingRequiredValue(key: key) | ||
} | ||
|
||
return dictionary.compactMapValues { T.convert(from: $0) } | ||
} | ||
|
||
/// Returns the value for the given key as an array of values converted using a``ClaimConvertable`` type. | ||
/// - Parameter key: String payload key name. | ||
/// - Returns: Value converted to an array of the requested type. | ||
func value<T: ClaimConvertable>(for claim: ClaimType) throws -> [String: T] { | ||
try value(for: claim.rawValue) | ||
} | ||
|
||
/// Returns the optional value for the given key as an array of values converted using a``ClaimConvertable`` type. | ||
/// - Parameter key: String payload key name. | ||
/// - Returns: Optional value converted to an array of the requested type. | ||
func value<T: ClaimConvertable>(for key: String) -> [String: T]? { | ||
let dictionary = payload[key] as? [String: ClaimConvertable] | ||
return dictionary?.compactMapValues { T.convert(from: $0) } | ||
} | ||
|
||
/// Returns the optional value for the given key as an array of values converted using a``ClaimConvertable`` type. | ||
/// - Parameter key: String payload key name. | ||
/// - Returns: Optional value converted to an array of the requested type. | ||
func value<T: ClaimConvertable>(for claim: ClaimType) -> [String: T]? { | ||
value(for: claim.rawValue) | ||
} | ||
} | ||
|
||
/// Extension which introduces single-value subscript conversion functions for ``ClaimConvertable`` types. | ||
public extension HasClaims { | ||
/// Return the given claim's value, defined with the given enum value, as the expectred ``ClaimConvertable``value type. | ||
subscript<T: ClaimConvertable>(_ claim: ClaimType) -> T? { | ||
value(for: claim) | ||
} | ||
|
||
/// Return the given claim's value, defined with the given enum value, as the expectred ``ClaimConvertable``value type. | ||
subscript<T: ClaimConvertable>(_ key: String) -> T? { | ||
value(for: key) | ||
} | ||
} | ||
|
||
/// Extension which introduces array-value subscript conversion functions for ``ClaimConvertable`` types. | ||
public extension HasClaims { | ||
/// Return the given claim's value, defined with the given enum value, as the expectred ``ClaimConvertable``value type. | ||
subscript<T: ClaimConvertable>(_ claim: ClaimType) -> [T]? { | ||
value(for: claim) | ||
} | ||
|
||
/// Return the given claim's value, defined with the given enum value, as the expectred ``ClaimConvertable``value type. | ||
subscript<T: ClaimConvertable>(_ key: String) -> [T]? { | ||
value(for: key) | ||
} | ||
} | ||
|
||
/// Extension which introduces dictionary-value subscript conversion functions for ``ClaimConvertable`` types. | ||
public extension HasClaims { | ||
/// Return the given claim's value, defined with the given enum value, as the expectred ``ClaimConvertable``value type. | ||
subscript<T: ClaimConvertable>(_ claim: ClaimType) -> [String: T]? { | ||
value(for: claim) | ||
} | ||
|
||
/// Return the given claim's value, defined with the given enum value, as the expectred ``ClaimConvertable``value type. | ||
subscript<T: ClaimConvertable>(_ key: String) -> [String: T]? { | ||
value(for: key) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,25 +12,42 @@ | |
|
||
import Foundation | ||
|
||
@_documentation(visibility: private) | ||
extension String: ClaimConvertable {} | ||
|
||
@_documentation(visibility: private) | ||
extension Bool: ClaimConvertable {} | ||
|
||
@_documentation(visibility: private) | ||
extension Int: ClaimConvertable {} | ||
|
||
@_documentation(visibility: private) | ||
extension Double: ClaimConvertable {} | ||
|
||
@_documentation(visibility: private) | ||
extension Float: ClaimConvertable {} | ||
extension Array<String>: ClaimConvertable {} | ||
extension Dictionary<String, String>: ClaimConvertable {} | ||
Comment on lines
-20
to
-21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does JWTClaim take care of array and dictionary? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's unnecessary since the accessors for working with claims (e.g. The goal for ClaimConvertable is to map some JSON primitive supplied from the server into a more developer-friendly value (e.g. from a string to In fact, the only reason Array and Dictionary were made to be convertable in the first place was because the |
||
|
||
@_documentation(visibility: private) | ||
extension URL: ClaimConvertable {} | ||
|
||
@_documentation(visibility: private) | ||
extension Date: ClaimConvertable {} | ||
|
||
@_documentation(visibility: private) | ||
extension JWTClaim: ClaimConvertable {} | ||
|
||
@_documentation(visibility: private) | ||
extension GrantType: ClaimConvertable {} | ||
|
||
@_documentation(visibility: private) | ||
extension NSString: ClaimConvertable {} | ||
|
||
@_documentation(visibility: private) | ||
extension NSNumber: ClaimConvertable {} | ||
|
||
@_documentation(visibility: private) | ||
extension ClaimConvertable where Self == Date { | ||
public static func claim(_ claim: String, | ||
in type: any HasClaims, | ||
from value: Any?) -> Self? | ||
{ | ||
public static func convert(from value: Any?) -> Self? { | ||
if let time = value as? Int { | ||
return Date(timeIntervalSince1970: TimeInterval(time)) | ||
} | ||
|
@@ -43,22 +60,25 @@ extension ClaimConvertable where Self == Date { | |
} | ||
} | ||
|
||
@_documentation(visibility: private) | ||
extension ClaimConvertable where Self == URL { | ||
public static func claim(_ claim: String, | ||
in type: any HasClaims, | ||
from value: Any?) -> Self? | ||
{ | ||
public static func convert(from value: Any?) -> Self? { | ||
guard let string = value as? String else { return nil } | ||
return URL(string: string) | ||
} | ||
} | ||
|
||
extension ClaimConvertable where Self: IsClaim { | ||
public static func claim(_ claim: String, | ||
in type: any HasClaims, | ||
from value: Any?) -> Self? | ||
{ | ||
guard let value = value as? String else { return nil } | ||
return .init(rawValue: value) | ||
@_documentation(visibility: private) | ||
extension ClaimConvertable where Self: RawRepresentable { | ||
public static func convert(from value: Any?) -> Self? { | ||
if let value = value as? Self { | ||
return value | ||
} | ||
|
||
if let value = value as? Self.RawValue { | ||
return Self(rawValue: value) | ||
} | ||
|
||
return nil | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
curious, so this hides the extension from generated docs or from Xcode as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From the generated docs. Many of these just clutter up the docs. For reference, here's the list of underscored attributes.