feature/Yahoo as an OpenID Connect provider

This commit is contained in:
Marko Milić 2020-05-12 05:40:06 +02:00
parent 92a29971e6
commit 48f2d28793
6 changed files with 127 additions and 4 deletions

View File

@ -468,7 +468,7 @@ There are 3 API's endpoint related to webhooks:
## OpenID Connect
In order to enable an OIDC workflow at an instance of OBP-API portal app(login functionality) you need to set-up the following props:
```props
# Google as an identity provider
## Google as an identity provider
# openid_connect_1.client_secret=OYdWujJl******_NXzPlDI4T
# openid_connect_1.client_id=883**3244***-s4hi72j0rble0iiivq1gn09k7***tdci.apps.googleusercontent.com
# openid_connect_1.callback_url=http://127.0.0.1:8080/auth/openid-connect/callback
@ -477,6 +477,18 @@ In order to enable an OIDC workflow at an instance of OBP-API portal app(login f
# openid_connect_1.endpoint.token=https://oauth2.googleapis.com/token
# openid_connect_1.endpoint.jwks_uri=https://www.googleapis.com/oauth2/v3/certs
# openid_connect_1.access_type_offline=false
# openid_connect_1.button_text = Yahoo
## Yahoo as an identity provider
# openid_connect_2.client_secret=685d47412efd8b74891ad711876558189793e957
# openid_connect_2.client_id=zg0yJmk9WUEzaERzd1RtMU02JmQ9WVdrOU9FOHpTbXN5TkhNbWNHbzlNQS0tJnM9Y38uc3VtZXJzZWNyZXQmc3Y9MCZ4PWjW
# openid_connect_2.callback_url=https://1aaac045.ngrok.io/auth/openid-connect/callback-2
# openid_connect_2.endpoint.authorization=https://api.login.yahoo.com/oauth2/request_auth
# openid_connect_2.endpoint.userinfo=https://api.login.yahoo.com/openid/v1/userinfo
# openid_connect_2.endpoint.token=https://api.login.yahoo.com/oauth2/get_token
# openid_connect_2.endpoint.jwks_uri=https://api.login.yahoo.com/openid/v1/certs
# openid_connect_2.access_type_offline=true
# openid_connect_2.button_text = Yahoo
```
Please note in the example above you MUST run OBP-API portal at the URL: http://127.0.0.1:8080

View File

@ -126,6 +126,25 @@ object JwtUtil extends MdcLoggable {
}
}
/**
* The Issuer Identifier for the Issuer of the response.
* Get the value of the "iss" claim, or None if it's not available.
*
* @return the Issuer's value or None.
*/
def getAlgorithm(jwtToken: String): Option[JWSAlgorithm] = {
try {
val signedJWT = SignedJWT.parse(jwtToken)
// claims extraction...
Some(signedJWT.getHeader().getAlgorithm())
} catch {
case e: Exception =>
logger.error(msg = "code.api.util.JwtUtil.getAlgorithm")
logger.error(e)
None
}
}
/**
* This function validates Access Token
* @param accessToken The access token to validate, typically submitted with a HTTP header like
@ -186,7 +205,7 @@ object JwtUtil extends MdcLoggable {
val iss: Issuer = new Issuer(getIssuer(idToken).getOrElse(""))
val aud = getAudience(idToken).headOption.getOrElse("")
val clientID: ClientID = new ClientID(aud)
val jwsAlg: JWSAlgorithm = JWSAlgorithm.RS256
val jwsAlg: JWSAlgorithm = getAlgorithm(idToken).getOrElse(JWSAlgorithm.RS256)
val jwkSetURL: URL = new URL(remoteJWKSetUrl)
// Create validator for signed ID tokens

View File

@ -64,6 +64,7 @@ object Migration extends MdcLoggable {
bankAccountHoldersAndOwnerViewAccessInfo()
alterTableMappedConsent()
alterColumnChallengeAtTableMappedConsent()
alterTableOpenIDConnectToken()
}
private def dummyScript(): Boolean = {
@ -158,6 +159,13 @@ object Migration extends MdcLoggable {
MigrationOfMappedConsent.alterColumnChallenge(name)
}
}
private def alterTableOpenIDConnectToken(): Boolean = {
val name = nameOf(alterTableOpenIDConnectToken)
runOnce(name) {
MigrationOfOpnIDConnectToken.alterColumnAccessToken(name)
MigrationOfOpnIDConnectToken.alterColumnRefreshToken(name)
}
}
}

View File

@ -0,0 +1,83 @@
package code.api.util.migration
import java.time.format.DateTimeFormatter
import java.time.{ZoneId, ZonedDateTime}
import code.api.util.APIUtil
import code.api.util.migration.Migration.{DbFunction, saveLog}
import code.token.OpenIDConnectToken
import net.liftweb.mapper.{DB, Schemifier}
import net.liftweb.util.DefaultConnectionIdentifier
object MigrationOfOpnIDConnectToken {
val oneDayAgo = ZonedDateTime.now(ZoneId.of("UTC")).minusDays(1)
val oneYearInFuture = ZonedDateTime.now(ZoneId.of("UTC")).plusYears(1)
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm'Z'")
def alterColumnAccessToken(name: String): Boolean = {
DbFunction.tableExists(OpenIDConnectToken, (DB.use(DefaultConnectionIdentifier){ conn => conn})) match {
case true =>
val startDate = System.currentTimeMillis()
val commitId: String = APIUtil.gitCommit
var isSuccessful = false
val executedSql =
DbFunction.maybeWrite(true, Schemifier.infoF _, DB.use(DefaultConnectionIdentifier){ conn => conn}) {
() => "ALTER TABLE openidconnecttoken ALTER COLUMN accesstoken type text;"
}
val endDate = System.currentTimeMillis()
val comment: String =
s"""Executed SQL:
|$executedSql
|""".stripMargin
isSuccessful = true
saveLog(name, commitId, isSuccessful, startDate, endDate, comment)
isSuccessful
case false =>
val startDate = System.currentTimeMillis()
val commitId: String = APIUtil.gitCommit
val isSuccessful = false
val endDate = System.currentTimeMillis()
val comment: String =
s"""${OpenIDConnectToken._dbTableNameLC} table does not exist""".stripMargin
saveLog(name, commitId, isSuccessful, startDate, endDate, comment)
isSuccessful
}
}
def alterColumnRefreshToken(name: String): Boolean = {
DbFunction.tableExists(OpenIDConnectToken, (DB.use(DefaultConnectionIdentifier){ conn => conn})) match {
case true =>
val startDate = System.currentTimeMillis()
val commitId: String = APIUtil.gitCommit
var isSuccessful = false
val executedSql =
DbFunction.maybeWrite(true, Schemifier.infoF _, DB.use(DefaultConnectionIdentifier){ conn => conn}) {
() => "ALTER TABLE openidconnecttoken ALTER COLUMN refreshtoken type text;"
}
val endDate = System.currentTimeMillis()
val comment: String =
s"""Executed SQL:
|$executedSql
|""".stripMargin
isSuccessful = true
saveLog(name, commitId, isSuccessful, startDate, endDate, comment)
isSuccessful
case false =>
val startDate = System.currentTimeMillis()
val commitId: String = APIUtil.gitCommit
val isSuccessful = false
val endDate = System.currentTimeMillis()
val comment: String =
s"""${OpenIDConnectToken._dbTableNameLC} table does not exist""".stripMargin
saveLog(name, commitId, isSuccessful, startDate, endDate, comment)
isSuccessful
}
}
}

View File

@ -318,6 +318,7 @@ import net.liftweb.util.Helpers._
def getCurrentUserUsername: String = {
getCurrentUser match {
case Full(user) if user.provider.contains("google") => user.emailAddress
case Full(user) if user.provider.contains("yahoo") => user.emailAddress
case Full(user) => user.name
case _ => "" //TODO need more error handling for different user cases
}

View File

@ -25,9 +25,9 @@ object MappedOpenIDConnectTokensProvider extends OpenIDConnectTokensProvider {
class OpenIDConnectToken extends OpenIDConnectTokenTrait with LongKeyedMapper[OpenIDConnectToken] with IdPK with CreatedUpdated {
def getSingleton: OpenIDConnectToken.type = OpenIDConnectToken
object AccessToken extends MappedString(this, 1024)
object AccessToken extends MappedText(this)
object IDToken extends MappedText(this)
object RefreshToken extends MappedString(this, 1024)
object RefreshToken extends MappedText(this)
object Scope extends MappedString(this, 250)
object TokenType extends MappedString(this, 250)
object ExpiresIn extends MappedLong(this)