Giter Site home page Giter Site logo

oidc4s's Introduction

oidc4s โ€” Open ID Connect 1.0 token verification for Scala

License Maven Central Known Vulnerabilities

This library can be used to verify and decode ID Tokens from Open ID Connect 1.0 providers like Google, Microsoft, Apple, Auth0, Okta.

Combined with OAuth 2.0 Authorization Code flow with PKCE on front-end side can lead to a simple, yet secure authentication for Single Page Applications.

Scala versions 3.x and 2.13.x are supported.

JWT Scala is used for JWT verification under-the-hood.

Getting started

To use this library with default Sttp/Circe implementations, add the following dependency to your build.sbt:

"me.wojnowski" %% "oidc4s-quick-sttp-circe" % "x.y.z"

Creating IdTokenVerifier instance

The next step depends on the runtime.

Cats Effect

For any effect with an existing implementation of Sync (e.g. Cats IO, ZIO, monix Task/IO), a version with cats.effect.Ref-based Cache can be used, for example:

import me.wojnowski.oidc4s.IdTokenVerifier
import me.wojnowski.oidc4s.config.Location

for {
  location        <- Sync[F].fromEither(Location.create("https://accounts.google.com"))
  backend         <- AsyncHttpClientCatsBackend[F]() // from async-http-client-backend-cats
  idTokenVerifier <- SttpCirceIdTokenVerifier.cachedWithCatsRef(location)(backend)
} yield idTokenVerifier

Id/Try/Either

For entirely synchronous, impure usage, a combination of import me.wojnowski.oidc4s.impure.implicits._ import and AtomicReference-based Cache instance can be used, for example:

import me.wojnowski.oidc4s.IdTokenVerifier
import me.wojnowski.oidc4s.config.Location
import me.wojnowski.oidc4s.impure.implicits._
import sttp.client3.HttpClientSyncBackend

val backend: SttpBackend[Identity, Any] = HttpClientSyncBackend()

val verifier = SttpCirceIdTokenVerifier.cachedWithAtomicRef[Id](Location.unsafeCreate("https://accounts.google.com"))(backend)

While the example shows Id implementation, Try and Either would be very similar, but different SttpBackend is needed.

Verifying a token

import me.wojnowski.oidc4s.IdTokenVerifier
import me.wojnowski.oidc4s.ClientId
import me.wojnowski.oidc4s.IdTokenClaims.Subject

val verifier: IdTokenVerifier[F] = ...

val clientId = ClientId("<client-id>")

val rawToken: String = "eyJhdWQ..."

val result: F[Either[IdTokenVerifier.Error, Subject]] = verifier.verify(rawToken, clientId)

Token verification and decoding

There are a few verification methods. Choosing one mostly comes down to which claims need to be decoded.

Subject only (verify)

To just verify a token is valid and matches the client ID use verify method. It returns Subject (usually some kind of user ID):

def verify(rawToken: String, expectedClientId: ClientId): F[Either[IdTokenVerifier.Error, IdTokenClaims.Subject]]

Standard claims (verifyAndDecode)

When more information is needed, a version with standard ID token claims can be used.

def verifyAndDecode(rawToken: String): F[Either[IdTokenVerifier.Error, IdTokenClaims]]

Custom claims (verifyAndDecodeCustom[A])

For full flexibility, verifyAndDecodeCustom method can be used. It requires ClaimsDecoder[A] instance, which can be derived from JSON-library-specific decoder (see below for Circe example).

def verifyAndDecodeCustom[A](rawToken: String)(implicit decoder: ClaimsDecoder[A]): F[Either[IdTokenVerifier.Error, A]]

There is also a version verifying if a token matches client ID:

def verifyAndDecodeCustom[A](rawToken: String, expectedClientId: ClientId)(implicit decoder: ClaimsDecoder[A]): F[Either[IdTokenVerifier.Error, A]]

Circe Example

case class CustomData(email: String, isAdmin: Boolean)
implicit val decoder: io.circe.Decoder[CustomData] = io.circe.generic.semiauto.deriveDecoder

import CirceJsonSupport._ // brings ClaimsDecoder[A: Decoder] instance into scope

val verifier: IdTokenVerifier[F] = ...
val rawToken: String = ...

val result: F[Either[IdTokenVerifier.Error, CustomData]] = verifier.verifyAndDecodeCustom[CustomData](rawToken)

Configuration Discovery (and caching)

Open ID Connect provider configuration (issuer and JWKS URL) is read from Open ID Configuration Document <location>/.well-known/openid-configuration ( e.g. https://login.microsoftonline.com/common/.well-known/openid-configuration)

If Cache is used, the configuration is cached according to HTTP cache headers with fallback to the (configurable) default of 1 day.

Public Key retrieval (and caching)

Signing keys are retrieved based on JWKS URL from the config. If Cache is used, they are cached indefinitely. However, if a new key is encountered, all keys are replaced with newly read keys. This ensures both the retrieval of new keys, and eventual removal of no longer used keys.

Modules and dependencies

The library has been modularised not to introduce too many dependencies, especially in terms of JSON decoding and HTTP layer. These are implemented in their own modules, and can be swapped out if needed.

Thus, oidc4s-core module defines Transport and JsonSupport abstractions. Currently, JsonSupport is implemented with Circe and Transport with sttp, which can be heavily customised on its own.

There are plans to add integrations with ZIO (facades and layers for ease of use) and zio-json as JsonSupport.

Currently available modules:

"me.wojnowski" %% "oidc4s-core" % "x.y.z"
"me.wojnowski" %% "oidc4s-circe" % "x.y.z"
"me.wojnowski" %% "oidc4s-sttp" % "x.y.z"

There's also an aggregation layer exposing handy constructors:

"me.wojnowski" %% "oidc4s-quick-circe-sttp" % "x.y.z"

Versioning and binary compatibility

For versions 0.x.y binary compatibility might be broken.

Version history starts from 0.5.0, as the core of this lib was previously a part of googlecloud4s.

oidc4s's People

Contributors

jwojnowski avatar scala-steward avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.