Skip to content
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

✨ Add verifyAndDecode variant with client check [breaking] #74

Merged
merged 3 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ inThisBuild(

val commonSettings = Seq(
makePom / publishArtifact := true,
mimaPreviousArtifacts := previousStableVersion.value.map(organization.value %% moduleName.value % _).toSet
// mimaPreviousArtifacts := previousStableVersion.value.map(organization.value %% moduleName.value % _).toSet
mimaPreviousArtifacts := Set.empty
)

lazy val Versions = new {
Expand Down
16 changes: 8 additions & 8 deletions core/src/main/scala/me/wojnowski/oidc4s/IdTokenVerifier.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ trait IdTokenVerifier[F[_]] {
/** Verifies a token is valid. Returns standard Open ID Token claims. Client ID must be checked manually. */
def verifyAndDecode(rawToken: String): F[Either[IdTokenVerifier.Error, IdTokenClaims]]

/** Verifies a token is valid. Returns standard Open ID Token claims. Client ID is verified. */
def verifyAndDecode(rawToken: String, expectedClientId: ClientId): F[Either[IdTokenVerifier.Error, IdTokenClaims]]

/** Verifies a token is valid. Returns custom type decoded using provided decoder.
*/
def verifyAndDecodeCustom[A](rawToken: String)(implicit decoder: ClaimsDecoder[A]): F[Either[IdTokenVerifier.Error, A]]
Expand All @@ -39,14 +42,6 @@ trait IdTokenVerifier[F[_]] {

object IdTokenVerifier {

@deprecated("Use instance", "0.11.0")
def create[F[_]: Monad: Clock](
publicKeyProvider: PublicKeyProvider[F],
discovery: OpenIdConnectDiscovery[F],
jsonSupport: JsonSupport
): IdTokenVerifier[F] =
this.discovery(publicKeyProvider, discovery, jsonSupport)

def discovery[F[_]: Monad: Clock](
publicKeyProvider: PublicKeyProvider[F],
discovery: OpenIdConnectDiscovery[F],
Expand Down Expand Up @@ -79,6 +74,11 @@ object IdTokenVerifier {
override def verifyAndDecode(rawToken: String): F[Either[IdTokenVerifier.Error, IdTokenClaims]] =
verifyAndDecodeCustom[IdTokenClaims](rawToken)(JsonDecoder[IdTokenClaims].decode(_).map(result => (result, result)))

override def verifyAndDecode(rawToken: String, expectedClientId: ClientId): F[Either[IdTokenVerifier.Error, IdTokenClaims]] =
verifyAndDecodeCustom[IdTokenClaims](rawToken, expectedClientId)(
JsonDecoder[IdTokenClaims].decode(_).map(result => (result, result))
)

override def verifyAndDecodeCustom[A](rawToken: String)(implicit decoder: ClaimsDecoder[A]): F[Either[Error, A]] =
internalVerifyAndDecode(rawToken, _ => Either.unit)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,6 @@ object PublicKeyProvider {
.traverse { case (keyId, key) => KeyUtils.parsePublicPemKey(key).map(keyId -> _) }
.map(keyIdToKeyList => static(keyIdToKeyList.toMap))

@deprecated("Use discovery", "0.11.0")
def jwks[F[_]: Monad](
discovery: OpenIdConnectDiscovery[F]
)(
transport: Transport[F],
jsonSupport: JsonSupport
): PublicKeyProvider[F] = this.discovery(discovery)(transport, jsonSupport)

def discovery[F[_]: Monad](
discovery: OpenIdConnectDiscovery[F]
)(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ object IdTokenVerifierMock {
}
.pure[F]

override def verifyAndDecode(rawToken: String, expectedClientId: ClientId): F[Either[IdTokenVerifier.Error, IdTokenClaims]] =
verifyAndDecode(rawToken).map(_.filterOrElse(_.matchesClientId(expectedClientId), IdTokenVerifier.Error.ClientIdDoesNotMatch))

override def verifyAndDecodeCustom[A](rawToken: String)(implicit decoder: ClaimsDecoder[A]): F[Either[IdTokenVerifier.Error, A]] =
rawTokenToRawClaimsEither
.lift(rawToken)
Expand Down Expand Up @@ -86,30 +89,16 @@ object IdTokenVerifierMock {

}

@deprecated("Use version with explicit client ID")
def constSubject[F[_]: Applicative: Traverse: Clock](subject: IdTokenClaims.Subject): IdTokenVerifier[F] =
constSubject[F](subject, ClientId("https://example.com"))

def constSubject[F[_]: Applicative: Clock](subject: IdTokenClaims.Subject, clientId: ClientId = ClientId("https://example.com"))
: IdTokenVerifier[F] =
constSubjectEither[F](Right(subject), clientId)

@deprecated("Use version with explicit client ID")
def constSubjectEither[F[_]: Applicative: Traverse: Clock](errorOrSubject: Either[IdTokenVerifier.Error, IdTokenClaims.Subject])
: IdTokenVerifier[F] = constSubjectEither[F](errorOrSubject, ClientId("https://example.com"))

def constSubjectEither[F[_]: Applicative: Clock](
errorOrSubject: Either[IdTokenVerifier.Error, IdTokenClaims.Subject],
clientId: ClientId = ClientId("https://example.com")
): IdTokenVerifier[F] =
constSubjectPF[F]((_: String) => errorOrSubject, clientId)

@deprecated("Use version with explicit client ID")
def constSubjectPF[F[_]: Applicative: Traverse: Clock](
rawTokenToSubjectPF: PartialFunction[String, Either[IdTokenVerifier.Error, IdTokenClaims.Subject]]
): IdTokenVerifier[F] =
constSubjectPF[F](rawTokenToSubjectPF, ClientId("https://example.com"))

def constSubjectPF[F[_]: Applicative: Clock](
rawTokenToSubjectPF: PartialFunction[String, Either[IdTokenVerifier.Error, IdTokenClaims.Subject]],
clientId: ClientId = ClientId("https://example.com")
Expand All @@ -130,19 +119,6 @@ object IdTokenVerifierMock {
}
)

@deprecated("Use constClaims", "0.12.2")
def constStandardClaims[F[_]: Applicative: Traverse](claims: IdTokenClaims): IdTokenVerifier[F] = constClaims(claims)

@deprecated("Use constClaimsEither", "0.12.2")
def constStandardClaimsEither[F[_]: Applicative: Traverse](claimsEither: Either[IdTokenVerifier.Error, IdTokenClaims])
: IdTokenVerifier[F] =
constClaimsEither(claimsEither)

@deprecated("Use constClaimsEitherPF", "0.12.2")
def constStandardClaimsEitherPF[F[_]: Applicative: Traverse](
rawTokenToClaimsPF: PartialFunction[String, F[Either[IdTokenVerifier.Error, IdTokenClaims]]]
): IdTokenVerifier[F] = constClaimsEitherPF(rawTokenToClaimsPF)

def constClaims[F[_]: Applicative](claims: IdTokenClaims): IdTokenVerifier[F] = constClaimsEither(Right(claims))

def constClaimsEither[F[_]: Applicative](claimsEither: Either[IdTokenVerifier.Error, IdTokenClaims]): IdTokenVerifier[F] =
Expand All @@ -160,6 +136,9 @@ object IdTokenVerifierMock {
.sequence
)(_.flatten)

override def verifyAndDecode(rawToken: String, expectedClientId: ClientId): F[Either[IdTokenVerifier.Error, IdTokenClaims]] =
verifyAndDecode(rawToken).map(_.filterOrElse(_.matchesClientId(expectedClientId), IdTokenVerifier.Error.ClientIdDoesNotMatch))

override def verify(rawToken: String, expectedClientId: ClientId): F[Either[IdTokenVerifier.Error, IdTokenClaims.Subject]] =
Applicative[F].map(verifyAndDecode(rawToken)) {
_.flatMap { standardClaims =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class IdTokenVerifierMockTest extends FunSuite {
case `expiredToken` => Left(IdTokenVerifier.Error.TokenExpired(Instant.EPOCH.plusSeconds(30)))
}

val standardClaimsMock: IdTokenVerifier[Id] = IdTokenVerifierMock.constStandardClaimsEitherPF {
val standardClaimsMock: IdTokenVerifier[Id] = IdTokenVerifierMock.constClaimsEitherPF {
case `token1` => Right(standardClaims1)
case `expiredToken` => Left(IdTokenVerifier.Error.TokenExpired(Instant.EPOCH.plusSeconds(2237)))
}
Expand Down
Loading