Skip to content
This repository has been archived by the owner on Oct 29, 2024. It is now read-only.

Commit

Permalink
Merge pull request #7 from vamsii777/feature/jwk
Browse files Browse the repository at this point in the history
Enhancing Security with JWKS Integration for OAuth 2.0 and OpenID Connect
  • Loading branch information
vamsii777 authored Feb 1, 2024
2 parents 7140e69 + 4bbe58e commit caa5464
Show file tree
Hide file tree
Showing 52 changed files with 969 additions and 488 deletions.
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/vapor/vapor.git",
"state" : {
"revision" : "67fe736c37b0ad958b9d248f010cff6c1baa5c3a",
"version" : "4.89.3"
"revision" : "6db3d917b5ce5024a84eb265ef65691383305d70",
"version" : "4.90.0"
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ let package = Package(
)
],
dependencies: [
.package(url: "https://github.com/vapor/vapor.git", from: "4.89.3"),
.package(url: "https://github.com/vapor/vapor.git", from: "4.90.0"),
.package(url: "https://github.com/apple/swift-crypto.git", from: "3.1.0"),
.package(url: "https://github.com/vapor/jwt-kit.git", from: "4.13.1")
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ public struct EmptyCodeManager: CodeManager {
redirectURI: String,
scopes: [String]?,
codeChallenge: String?,
codeChallengeMethod: String?
codeChallengeMethod: String?,
nonce: String?
) async throws -> String {
return ""
}
Expand Down
10 changes: 10 additions & 0 deletions Sources/VaporOAuth/Models/JWK.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Foundation
import Vapor
import JWTKit

public final class JWKS: Content {
public let keys: [JWK]
public init(keys: [JWK]) {
self.keys = keys
}
}
2 changes: 1 addition & 1 deletion Sources/VaporOAuth/Models/OAuthClient.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Vapor

public final class OAuthClient: Extendable {
public struct OAuthClient {
public let clientID: String
public let redirectURIs: [String]?
public let clientSecret: String?
Expand Down
2 changes: 1 addition & 1 deletion Sources/VaporOAuth/Models/OAuthCode.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation

public final class OAuthCode {
public struct OAuthCode {
public let codeID: String
public let clientID: String
public let redirectURI: String
Expand Down
9 changes: 1 addition & 8 deletions Sources/VaporOAuth/Models/OAuthDeviceCode.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
//
// OAuthDeviceCode.swift
//
//
// Created by Vamsi Madduluri on 24/08/23.
//

import Foundation

public final class OAuthDeviceCode {
public struct OAuthDeviceCode {
public let deviceCodeID: String
public let userCode: String
public let clientID: String
Expand Down
124 changes: 58 additions & 66 deletions Sources/VaporOAuth/Models/OAuthDiscoveryDocument.swift
Original file line number Diff line number Diff line change
@@ -1,70 +1,46 @@
import Vapor

public final class OAuthDiscoveryDocument: Content {
public let issuer: String
public let authorizationEndpoint: String
public let tokenEndpoint: String
public let userInfoEndpoint: String
public let revocationEndpoint: String
public let introspectionEndpoint: String
public let jwksURI: String
public let registrationEndpoint: String
public let scopesSupported: [String]
public let responseTypesSupported: [String]
public let grantTypesSupported: [String]
public let tokenEndpointAuthMethodsSupported: [String]
public let tokenEndpointAuthSigningAlgValuesSupported: [String]
public let serviceDocumentation: String
public let uiLocalesSupported: [String]
public let opPolicyURI: String
public let opTosURI: String
public let subjectTypesSupported: [String]
public let claimsSupported: [String]

public struct OAuthDiscoveryDocument: Content {

public var issuer: String?
public var authorizationEndpoint: String?
public var tokenEndpoint: String?
public var userInfoEndpoint: String?
public var revocationEndpoint: String?
public var introspectionEndpoint: String?
public var jwksURI: String?
public var registrationEndpoint: String?
public var scopesSupported: [String]?
public var responseTypesSupported: [String]?
public var responseModesSupported: [String]?
public var grantTypesSupported: [String]?
public var acrValuesSupported: [String]?
public var idTokenEncryptionAlgValuesSupported: [String]?
public var idTokenEncryptionEncValuesSupported: [String]?
public var userinfoSigningAlgValuesSupported: [String]?
public var userinfoEncryptionAlgValuesSupported: [String]?
public var userinfoEncryptionEncValuesSupported: [String]?
public var requestObjectSigningAlgValuesSupported: [String]?
public var requestObjectEncryptionAlgValuesSupported: [String]?
public var requestObjectEncryptionEncValuesSupported: [String]?
public var tokenEndpointAuthMethodsSupported: [String]?
public var tokenEndpointAuthSigningAlgValuesSupported: [String]?
public var displayValuesSupported: [String]?
public var claimTypesSupported: [String]?
public var claimsSupported: [String]?
public var serviceDocumentation: String?
public var claimsLocalesSupported: [String]?
public var uiLocalesSupported: [String]?
public var claimsParameterSupported: Bool?
public var requestParameterSupported: Bool?
public var requestUriParameterSupported: Bool?
public var requireRequestUriRegistration: Bool?
public var opPolicyURI: String?
public var opTosURI: String?
public var extend: [String: Any] = [:]

public init(
issuer: String,
authorizationEndpoint: String,
tokenEndpoint: String,
userInfoEndpoint: String,
revocationEndpoint: String,
introspectionEndpoint: String,
jwksURI: String,
registrationEndpoint: String,
scopesSupported: [String],
responseTypesSupported: [String],
grantTypesSupported: [String],
tokenEndpointAuthMethodsSupported: [String],
tokenEndpointAuthSigningAlgValuesSupported: [String],
serviceDocumentation: String,
uiLocalesSupported: [String],
opPolicyURI: String,
opTosURI: String,
subjectTypesSupported: [String],
claimsSupported: [String]
) {
self.issuer = issuer
self.authorizationEndpoint = authorizationEndpoint
self.tokenEndpoint = tokenEndpoint
self.userInfoEndpoint = userInfoEndpoint
self.revocationEndpoint = revocationEndpoint
self.introspectionEndpoint = introspectionEndpoint
self.jwksURI = jwksURI
self.registrationEndpoint = registrationEndpoint
self.scopesSupported = scopesSupported
self.responseTypesSupported = responseTypesSupported
self.grantTypesSupported = grantTypesSupported
self.tokenEndpointAuthMethodsSupported = tokenEndpointAuthMethodsSupported
self.tokenEndpointAuthSigningAlgValuesSupported = tokenEndpointAuthSigningAlgValuesSupported
self.serviceDocumentation = serviceDocumentation
self.uiLocalesSupported = uiLocalesSupported
self.opPolicyURI = opPolicyURI
self.opTosURI = opTosURI
self.subjectTypesSupported = subjectTypesSupported
self.claimsSupported = claimsSupported
}




// Exclude 'extend' property from encoding
private enum CodingKeys: String, CodingKey {
case issuer
Expand All @@ -77,14 +53,30 @@ public final class OAuthDiscoveryDocument: Content {
case registrationEndpoint
case scopesSupported
case responseTypesSupported
case responseModesSupported
case grantTypesSupported
case acrValuesSupported
case idTokenEncryptionAlgValuesSupported
case idTokenEncryptionEncValuesSupported
case userinfoSigningAlgValuesSupported
case userinfoEncryptionAlgValuesSupported
case userinfoEncryptionEncValuesSupported
case requestObjectSigningAlgValuesSupported
case requestObjectEncryptionAlgValuesSupported
case requestObjectEncryptionEncValuesSupported
case tokenEndpointAuthMethodsSupported
case tokenEndpointAuthSigningAlgValuesSupported
case displayValuesSupported
case claimTypesSupported
case claimsSupported
case serviceDocumentation
case claimsLocalesSupported
case uiLocalesSupported
case claimsParameterSupported
case requestParameterSupported
case requestUriParameterSupported
case requireRequestUriRegistration
case opPolicyURI
case opTosURI
case subjectTypesSupported
case claimsSupported
}
}
2 changes: 1 addition & 1 deletion Sources/VaporOAuth/Models/OAuthResourceServer.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Vapor

public final class OAuthResourceServer: Extendable {
public struct OAuthResourceServer {
public let username: String
public let password: String
public var extend: Vapor.Extend = .init()
Expand Down
45 changes: 36 additions & 9 deletions Sources/VaporOAuth/Models/OAuthUser.swift
Original file line number Diff line number Diff line change
@@ -1,52 +1,79 @@
import Vapor

public final class OAuthUser: Authenticatable, Extendable, Encodable {
public struct OAuthUser: Authenticatable, Content {
public var id: String?
public let username: String
public let emailAddress: String?
public var password: String

public var id: String?

// OpenID Connect specific attributes
public var name: String?
public var givenName: String?
public var familyName: String?
public var middleName: String?
public var nickname: String?
public var preferredUserName: String?
public var profile: String?
public var picture: String?
public var website: String?
public var emailVerified: Bool?
public var gender: String?
public var birthdate: String?
public var zoneinfo: String?
public var locale: String?
public var phoneNumber: String?
public var phoneNumberVerified: Bool?
public var address: Address?
public var extend: [String: String]?
public var updatedAt: Date?

public var extend: Extend = .init()

public init(userID: String? = nil, username: String, emailAddress: String?, password: String,
name: String? = nil, givenName: String? = nil, familyName: String? = nil, middleName: String? = nil,
nickname: String? = nil, profile: String? = nil, picture: String? = nil, website: String? = nil,
gender: String? = nil, birthdate: String? = nil, zoneinfo: String? = nil, locale: String? = nil,
phoneNumber: String? = nil, updatedAt: Date? = nil) {
nickname: String? = nil, preferredUserName: String? = nil, profile: String? = nil, picture: String? = nil,
website: String? = nil, emailVerified: Bool? = nil, gender: String? = nil, birthdate: String? = nil,
zoneinfo: String? = nil, locale: String? = nil, phoneNumber: String? = nil, phoneNumberVerified: Bool? = nil,
address: Address? = nil, extend: [String: String]? = nil, updatedAt: Date? = nil) {
self.id = userID
self.username = username
self.emailAddress = emailAddress
self.password = password
self.id = userID
self.name = name
self.givenName = givenName
self.familyName = familyName
self.middleName = middleName
self.nickname = nickname
self.preferredUserName = preferredUserName
self.profile = profile
self.picture = picture
self.website = website
self.emailVerified = emailVerified
self.gender = gender
self.birthdate = birthdate
self.zoneinfo = zoneinfo
self.locale = locale
self.phoneNumber = phoneNumber
self.phoneNumberVerified = phoneNumberVerified
self.address = address
self.extend = extend
self.updatedAt = updatedAt
}
}

public struct Address: Content {
public var formatted: String?
public var streetAddress: String?
public var locality: String?
public var region: String?
public var postalCode: String?
public var country: String?

public init(formatted: String? = nil, streetAddress: String? = nil, locality: String? = nil,
region: String? = nil, postalCode: String? = nil, country: String? = nil) {
self.formatted = formatted
self.streetAddress = streetAddress
self.locality = locality
self.region = region
self.postalCode = postalCode
self.country = country
}
}
14 changes: 11 additions & 3 deletions Sources/VaporOAuth/Models/Tokens/AccessToken.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import Vapor
import JWTKit

public protocol AccessToken {
var tokenString: String { get }
public protocol AccessToken: JWTPayload {
var jti: String { get }
var clientID: String { get }
var userID: String? { get }
var scopes: [String]? { get }
var scopes: String? { get }
var expiryTime: Date { get }
}

// Providing a default implementation of verify(using:) for AccessToken
extension AccessToken {
public func verify(using signer: JWTSigner) throws {
try expiryTime.verifyNotExpired()
}
}
18 changes: 9 additions & 9 deletions Sources/VaporOAuth/Models/Tokens/IDToken.swift
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import JWTKit
import Vapor

public protocol IDToken {
var tokenString: String { get set }
var issuer: String { get set }
var subject: String { get set }
var audience: [String] { get set }
var expiration: Date { get set }
var issuedAt: Date { get set }
var nonce: String? { get set }
var authTime: Date? { get set }
public protocol IDToken: JWTPayload {
var jti: String { get set } // JWT ID, a unique identifier for the token
var iss: String { get set } // Issuer
var sub: String { get set } // Subject
var aud: [String] { get set } // Audience
var exp: Date { get set } // Expiration Time
var iat: Date { get set } // Issued At
var nonce: String? { get set } // Nonce, used in OpenID Connect
var authTime: Date? { get set } // Authentication Time
// Additional claims can be added as needed
}
15 changes: 12 additions & 3 deletions Sources/VaporOAuth/Models/Tokens/RefreshToken.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import Vapor
import JWTKit

public protocol RefreshToken {
var tokenString: String { get set }
public protocol RefreshToken: JWTPayload {
var jti: String { get set }
var clientID: String { get set }
var userID: String? { get set }
var scopes: [String]? { get set }
var scopes: String? { get set }
var exp: Date { get }
}

// Implementing verify(using:) for the RefreshToken protocol
extension RefreshToken {
public func verify(using signer: JWTSigner) throws {
try exp.verifyNotExpired()
}
}
Loading

0 comments on commit caa5464

Please sign in to comment.