2015-08-18 05:57:27 +05:30
package user
import (
"fmt"
"net/url"
"time"
"github.com/jonboulle/clockwork"
"github.com/coreos/go-oidc/jose"
"github.com/coreos/go-oidc/key"
"github.com/coreos/go-oidc/oidc"
)
var (
clock = clockwork . NewRealClock ( )
)
// NewEmailVerification creates an object which can be sent to a user in serialized form to verify that they control an email address.
// The clientID is the ID of the registering user. The callback is where a user should land after verifying their email.
func NewEmailVerification ( user User , clientID string , issuer url . URL , callback url . URL , expires time . Duration ) EmailVerification {
claims := oidc . NewClaims ( issuer . String ( ) , user . ID , clientID , clock . Now ( ) , clock . Now ( ) . Add ( expires ) )
claims . Add ( ClaimEmailVerificationCallback , callback . String ( ) )
claims . Add ( ClaimEmailVerificationEmail , user . Email )
return EmailVerification { claims }
}
type EmailVerification struct {
2015-10-17 03:17:58 +05:30
Claims jose . Claims
2015-08-18 05:57:27 +05:30
}
2015-10-17 03:17:58 +05:30
// Assumes that parseAndVerifyTokenClaims has already been called on claims
func verifyEmailVerificationClaims ( claims jose . Claims ) ( EmailVerification , error ) {
email , ok , err := claims . StringClaim ( ClaimEmailVerificationEmail )
2015-08-18 05:57:27 +05:30
if err != nil {
return EmailVerification { } , err
}
2015-10-17 03:17:58 +05:30
if ! ok || email == "" {
return EmailVerification { } , fmt . Errorf ( "no %q claim" , ClaimEmailVerificationEmail )
2015-08-18 05:57:27 +05:30
}
cb , ok , err := claims . StringClaim ( ClaimEmailVerificationCallback )
if err != nil {
return EmailVerification { } , err
}
2015-10-17 03:17:58 +05:30
if ! ok || cb == "" {
2015-08-18 05:57:27 +05:30
return EmailVerification { } , fmt . Errorf ( "no %q claim" , ClaimEmailVerificationCallback )
}
if _ , err := url . Parse ( cb ) ; err != nil {
return EmailVerification { } , fmt . Errorf ( "callback URL not parseable: %v" , cb )
}
2015-10-17 03:17:58 +05:30
return EmailVerification { claims } , nil
}
2015-08-18 05:57:27 +05:30
2015-10-17 03:17:58 +05:30
// ParseAndVerifyEmailVerificationToken parses a string into a an EmailVerification, verifies the signature, and ensures that required claims are present.
// In addition to the usual claims required by the OIDC spec, "aud" and "sub" must be present as well as ClaimEmailVerificationCallback and ClaimEmailVerificationEmail.
func ParseAndVerifyEmailVerificationToken ( token string , issuer url . URL , keys [ ] key . PublicKey ) ( EmailVerification , error ) {
tokenClaims , err := parseAndVerifyTokenClaims ( token , issuer , keys )
2015-08-18 05:57:27 +05:30
if err != nil {
return EmailVerification { } , err
}
2015-10-17 03:17:58 +05:30
return verifyEmailVerificationClaims ( tokenClaims . Claims )
2015-08-18 05:57:27 +05:30
}
func ( e EmailVerification ) UserID ( ) string {
2015-10-17 03:17:58 +05:30
uid , ok , err := e . Claims . StringClaim ( "sub" )
2015-08-18 05:57:27 +05:30
if ! ok || err != nil {
panic ( "EmailVerification: no sub claim. This should be impossible." )
}
return uid
}
func ( e EmailVerification ) Email ( ) string {
2015-10-17 03:17:58 +05:30
email , ok , err := e . Claims . StringClaim ( ClaimEmailVerificationEmail )
2015-08-18 05:57:27 +05:30
if ! ok || err != nil {
panic ( "EmailVerification: no email claim. This should be impossible." )
}
return email
}
func ( e EmailVerification ) Callback ( ) * url . URL {
2015-10-17 03:17:58 +05:30
cb , ok , err := e . Claims . StringClaim ( ClaimEmailVerificationCallback )
2015-08-18 05:57:27 +05:30
if ! ok || err != nil {
panic ( "EmailVerification: no callback claim. This should be impossible." )
}
cbURL , err := url . Parse ( cb )
if err != nil {
panic ( "EmailVerificaiton: can't parse callback. This should be impossible." )
}
return cbURL
}