2020-03-04 11:07:12 +00:00
package main
import (
"context"
"encoding/hex"
"flag"
"fmt"
"log"
"net/http"
"golang.org/x/oauth2"
2022-05-01 12:07:35 +00:00
"github.com/coreos/go-oidc/v3/oidc"
2022-07-09 17:42:00 +00:00
"github.com/gin-gonic/gin"
2020-03-04 11:07:12 +00:00
)
var (
2020-09-14 08:23:44 +00:00
oidcClientID = ""
oidcSecret = ""
2020-03-08 00:06:44 +00:00
oidcRedirectURL = "https://srs.nemunai.re"
2020-09-14 08:23:44 +00:00
oauth2Config oauth2 . Config
oidcVerifier * oidc . IDTokenVerifier
2022-05-15 10:36:17 +00:00
nextSessionMap = map [ string ] string { }
2020-03-04 11:07:12 +00:00
)
func init ( ) {
flag . StringVar ( & oidcClientID , "oidc-clientid" , oidcClientID , "ClientID for OIDC" )
flag . StringVar ( & oidcSecret , "oidc-secret" , oidcSecret , "Secret for OIDC" )
2020-03-08 00:06:44 +00:00
flag . StringVar ( & oidcRedirectURL , "oidc-redirect" , oidcRedirectURL , "Base URL for the redirect after connection" )
2022-07-09 17:42:00 +00:00
}
2020-03-04 11:07:12 +00:00
2022-07-09 17:42:00 +00:00
func initializeOIDC ( router * gin . Engine ) {
2020-03-04 11:07:12 +00:00
router . GET ( "/auth/CRI" , redirectOIDC_CRI )
router . GET ( "/auth/complete" , OIDC_CRI_complete )
if oidcClientID != "" && oidcSecret != "" {
2020-09-14 08:23:44 +00:00
provider , err := oidc . NewProvider ( context . Background ( ) , "https://cri.epita.fr" )
2020-03-04 11:07:12 +00:00
if err != nil {
log . Fatal ( "Unable to setup oidc:" , err )
}
oauth2Config = oauth2 . Config {
ClientID : oidcClientID ,
ClientSecret : oidcSecret ,
2020-03-08 00:06:44 +00:00
RedirectURL : oidcRedirectURL + baseURL + "/auth/complete" ,
2020-03-04 11:07:12 +00:00
// Discovery returns the OAuth2 endpoints.
Endpoint : provider . Endpoint ( ) ,
// "openid" is a required scope for OpenID Connect flows.
2020-11-24 13:06:29 +00:00
Scopes : [ ] string { oidc . ScopeOpenID , "profile" , "email" , "epita" } ,
2020-03-04 11:07:12 +00:00
}
oidcConfig := oidc . Config {
2020-09-14 08:23:44 +00:00
ClientID : oidcClientID ,
2020-03-04 11:07:12 +00:00
}
oidcVerifier = provider . Verifier ( & oidcConfig )
}
}
2022-07-09 17:42:00 +00:00
func redirectOIDC_CRI ( c * gin . Context ) {
2020-03-04 11:07:12 +00:00
session , err := NewSession ( )
2022-05-15 10:36:17 +00:00
// Save next parameter
2022-07-09 17:42:00 +00:00
if len ( c . Request . URL . Query ( ) . Get ( "next" ) ) > 0 {
nextSessionMap [ fmt . Sprintf ( "%x" , session . Id ) ] = c . Request . URL . Query ( ) . Get ( "next" )
2022-05-15 10:36:17 +00:00
}
2020-03-04 11:07:12 +00:00
if err != nil {
2022-07-09 17:42:00 +00:00
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : err . Error ( ) } )
return
2020-03-04 11:07:12 +00:00
}
2022-07-09 17:42:00 +00:00
c . Redirect ( http . StatusFound , oauth2Config . AuthCodeURL ( hex . EncodeToString ( session . Id ) ) )
2020-03-04 11:07:12 +00:00
}
2022-07-09 17:42:00 +00:00
func OIDC_CRI_complete ( c * gin . Context ) {
idsession , err := hex . DecodeString ( c . Request . URL . Query ( ) . Get ( "state" ) )
2020-03-04 11:07:12 +00:00
if err != nil {
2022-07-09 17:42:00 +00:00
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : err . Error ( ) } )
2020-03-04 11:07:12 +00:00
return
}
session , err := getSession ( idsession )
if err != nil {
2022-07-09 17:42:00 +00:00
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : err . Error ( ) } )
2020-03-04 11:07:12 +00:00
return
}
2022-09-04 11:23:11 +00:00
oauth2Token , err := oauth2Config . Exchange ( c . Request . Context ( ) , c . Request . URL . Query ( ) . Get ( "code" ) )
2020-03-04 11:07:12 +00:00
if err != nil {
2022-07-09 17:42:00 +00:00
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "Failed to exchange token: " + err . Error ( ) } )
2020-03-04 11:07:12 +00:00
return
}
rawIDToken , ok := oauth2Token . Extra ( "id_token" ) . ( string )
if ! ok {
2022-07-09 17:42:00 +00:00
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "No id_token field in oauth2 token." } )
2020-03-04 11:07:12 +00:00
return
}
2022-09-04 11:23:11 +00:00
idToken , err := oidcVerifier . Verify ( c . Request . Context ( ) , rawIDToken )
2020-03-04 11:07:12 +00:00
if err != nil {
2022-07-09 17:42:00 +00:00
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "Failed to verify ID Token: " + err . Error ( ) } )
2020-03-04 11:07:12 +00:00
return
}
var claims struct {
2022-11-11 10:14:43 +00:00
Firstname string ` json:"given_name" `
Lastname string ` json:"family_name" `
Username string ` json:"preferred_username" `
Email string ` json:"email" `
Groups [ ] map [ string ] interface { } ` json:"groups" `
Campuses [ ] string ` json:"campuses" `
GraduationYears [ ] uint ` json:"graduation_years" `
2020-03-04 11:07:12 +00:00
}
if err := idToken . Claims ( & claims ) ; err != nil {
2022-11-11 10:14:43 +00:00
log . Println ( "Unable to extract claims to Claims:" , err . Error ( ) )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "Something goes wrong when analyzing your claims. Contact administrator to fix the issue." } )
2020-03-04 11:07:12 +00:00
return
}
2021-09-15 22:26:09 +00:00
groups := ","
for _ , g := range claims . Groups {
if slug , ok := g [ "slug" ] ; ok {
groups += slug . ( string ) + ","
}
}
2021-01-09 14:06:26 +00:00
2022-11-11 10:14:43 +00:00
var promo uint
if len ( claims . GraduationYears ) > 0 {
for _ , gy := range claims . GraduationYears {
if gy > promo {
promo = gy
}
}
}
if _ , err := completeAuth ( c , claims . Username , claims . Email , claims . Firstname , claims . Lastname , promo , groups , session ) ; err != nil {
2022-07-09 17:42:00 +00:00
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : err . Error ( ) } )
2020-03-04 11:07:12 +00:00
return
}
2022-05-15 10:36:17 +00:00
// Retrieve next URL associated with session
if next , ok := nextSessionMap [ fmt . Sprintf ( "%x" , session . Id ) ] ; ok {
2022-07-09 17:42:00 +00:00
c . Redirect ( http . StatusFound , next )
2022-05-15 10:36:17 +00:00
delete ( nextSessionMap , fmt . Sprintf ( "%x" , session . Id ) )
} else {
2022-07-09 17:42:00 +00:00
c . Redirect ( http . StatusFound , "/" )
2022-05-15 10:36:17 +00:00
}
2020-03-04 11:07:12 +00:00
}