示例#1
0
func parseCodeToken(raw []byte) (*codeToken, error) {
	base, err := jwt.Parse(raw)
	if err != nil {
		return nil, erro.Wrap(err)
	}
	var buff struct {
		Idp      string            `json:"iss"`
		Cod      string            `json:"sub"`
		Aud      audience.Audience `json:"aud"`
		FrTa     string            `json:"from_client"`
		AcntTag  string            `json:"user_tag"`
		AcntTags strset.Set        `json:"user_tags"`
		RefHash  string            `json:"ref_hash"`
	}
	if err := json.Unmarshal(base.RawBody(), &buff); err != nil {
		return nil, erro.Wrap(err)
	} else if buff.Idp == "" {
		return nil, erro.New("no ID provider ID")
	} else if buff.Cod == "" {
		return nil, erro.New("no code")
	} else if len(buff.Aud) == 0 {
		return nil, erro.New("no audience")
	}

	return &codeToken{
		base:     base,
		idp:      buff.Idp,
		cod:      buff.Cod,
		aud:      buff.Aud,
		frTa:     buff.FrTa,
		acntTag:  buff.AcntTag,
		acntTags: buff.AcntTags,
		refHash:  buff.RefHash,
	}, nil
}
示例#2
0
func parseIdToken(raw []byte) (*idToken, error) {
	base, err := jwt.Parse(raw)
	if err != nil {
		return nil, erro.Wrap(err)
	}
	alg, _ := base.Header(tagAlg).(string)
	if alg == "" {
		return nil, erro.New("no alg")
	}
	var buff struct {
		Idp    string `json:"iss"`
		Nonc   string `json:"nonce"`
		CHash  string `json:"c_hash"`
		AtHash string `json:"at_hash"`
	}
	if err := json.Unmarshal(base.RawBody(), &buff); err != nil {
		return nil, erro.Wrap(err)
	} else if buff.Idp == "" {
		return nil, erro.New("no ID provider ID")
	} else if buff.Nonc == "" {
		return nil, erro.New("no nonce")
	}
	var cHash, atHash []byte
	if buff.CHash != "" {
		cHash, err = base64.RawURLEncoding.DecodeString(buff.CHash)
		if err != nil {
			return nil, erro.Wrap(err)
		}
	}
	if buff.AtHash != "" {
		atHash, err = base64.RawURLEncoding.DecodeString(buff.AtHash)
		if err != nil {
			return nil, erro.Wrap(err)
		}
	}

	return &idToken{
		base:   base,
		alg:    alg,
		idp:    buff.Idp,
		nonc:   buff.Nonc,
		cHash:  cHash,
		atHash: atHash,
	}, nil
}
示例#3
0
func parseIdsToken(raw []byte) (*idsToken, error) {
	base, err := jwt.Parse(raw)
	if err != nil {
		return nil, erro.Wrap(err)
	}

	var buff struct {
		Idp        string                            `json:"iss"`
		FrTa       string                            `json:"sub"`
		Aud        audience.Audience                 `json:"aud"`
		Exp        int64                             `json:"exp"`
		Date       int64                             `json:"iat"`
		TagToAttrs map[string]map[string]interface{} `json:"ids"`
	}
	if err := json.Unmarshal(base.RawBody(), &buff); err != nil {
		return nil, erro.Wrap(err)
	} else if buff.Idp == "" {
		return nil, erro.New("no ID provider ID")
	} else if buff.FrTa == "" {
		return nil, erro.New("no from-TA ID")
	} else if buff.Aud == nil {
		return nil, erro.New("no audience")
	} else if buff.Exp == 0 {
		return nil, erro.New("no expiration date")
	} else if buff.Date == 0 {
		return nil, erro.New("no published date")
	} else if len(buff.TagToAttrs) == 0 {
		return nil, erro.New("no IDs")
	}
	return &idsToken{
		base:       base,
		idp:        buff.Idp,
		frTa:       buff.FrTa,
		aud:        buff.Aud,
		exp:        time.Unix(buff.Exp, 0),
		date_:      time.Unix(buff.Date, 0),
		tagToAttrs: buff.TagToAttrs,
	}, nil
}
示例#4
0
// 仲介コードが 2 つ以上の場合の正常系。
// レスポンスが X-Auth-User, X-Auth-User-Tag, X-Auth-Users, X-Auth-From-Id を含むことの検査。
// 主体情報が iss, sub, at_tag, at_exp クレームを含むことの検査。
// 主体でないアカウント情報が iss, sub クレームを含むことの検査。
func TestMultiNormal(t *testing.T) {
	// ////////////////////////////////
	// logutil.SetupConsole(logRoot, level.ALL)
	// defer logutil.SetupConsole(logRoot, level.OFF)
	// ////////////////////////////////

	idpServ, err := newTestIdProvider([]jwk.Key{test_idpKey})
	if err != nil {
		t.Fatal(err)
	}
	defer idpServ.close()
	idp := idpServ.info()
	subIdpServ, err := newTestIdProvider([]jwk.Key{test_subIdpKey})
	if err != nil {
		t.Fatal(err)
	}
	defer subIdpServ.close()
	subIdp := subIdpServ.info()
	hndl := newTestHandler([]jwk.Key{test_toTaKey}, []idpdb.Element{idp, subIdp})

	r, err := newTestRequest(hndl, idp, subIdp)
	if err != nil {
		t.Fatal(err)
	}

	var reqCh <-chan *http.Request
	{
		s, h, b, err := newTestMainIdpResponse(hndl, idp)
		if err != nil {
			t.Fatal(err)
		}
		reqCh = idpServ.addResponse(s, h, b)
	}
	var subReqCh <-chan *http.Request
	{
		s, h, b, err := newTestSubIdpResponse(hndl, subIdp)
		if err != nil {
			t.Fatal(err)
		}
		subReqCh = subIdpServ.addResponse(s, h, b)
	}

	w := httptest.NewRecorder()
	hndl.ServeHTTP(w, r)

	select {
	case req := <-reqCh:
		if contType, contType2 := "application/json", req.Header.Get("Content-Type"); contType2 != contType {
			t.Error(contType)
			t.Fatal(contType2)
		}
		var buff struct {
			Grant_type  string
			Code        string
			Claims      *claims.Request
			User_claims claims.Claims
		}
		if err := json.NewDecoder(req.Body).Decode(&buff); err != nil {
			t.Fatal(err)
		} else if grntType := "cooperation_code"; buff.Grant_type != grntType {
			t.Error(buff.Grant_type)
			t.Fatal(grntType)
		} else if buff.Code != test_cod {
			t.Error(buff.Code)
			t.Fatal(test_cod)
		}
	case <-time.After(time.Minute):
		t.Fatal("no request")
	}
	select {
	case req := <-subReqCh:
		if contType, contType2 := "application/json", req.Header.Get("Content-Type"); contType2 != contType {
			t.Error(contType)
			t.Fatal(contType2)
		}
		var buff struct {
			Grant_type  string
			Code        string
			Claims      *claims.Request
			User_claims claims.Claims
		}
		if err := json.NewDecoder(req.Body).Decode(&buff); err != nil {
			t.Fatal(err)
		} else if grntType := "cooperation_code"; buff.Grant_type != grntType {
			t.Error(buff.Grant_type)
			t.Fatal(grntType)
		} else if buff.Code != test_subCod {
			t.Error(buff.Code)
			t.Fatal(test_subCod)
		}
	case <-time.After(time.Minute):
		t.Fatal("no request")
	}

	if w.Code != http.StatusOK {
		t.Error(w.Code)
		t.Fatal(http.StatusOK)
	}
	acntJt, err := jwt.Parse([]byte(w.HeaderMap.Get("X-Auth-User")))
	if err != nil {
		t.Fatal(err)
	}
	var acntBuff struct {
		Iss    string
		Sub    string
		At_tag string
		At_exp int64
		Email  string
	}
	if err := json.Unmarshal(acntJt.RawBody(), &acntBuff); err != nil {
		t.Fatal(err)
	} else if acntBuff.Iss != idp.Id() {
		t.Error(acntBuff.Iss)
		t.Fatal(idp.Id())
	} else if acntBuff.Sub != test_acntId {
		t.Error(acntBuff.Sub)
		t.Fatal(test_acntId)
	} else if acntBuff.At_tag == "" {
		t.Fatal("no token tag")
	} else if now, exp := time.Now(), time.Unix(acntBuff.At_exp, 0); exp.Before(now) {
		t.Error("expired")
		t.Error(now)
		t.Fatal(exp)
	} else if acntBuff.Email != test_acntEmail {
		t.Error(acntBuff.Email)
		t.Fatal(test_acntEmail)
	}
	acntTag := w.HeaderMap.Get("X-Auth-User-Tag")
	if acntTag != test_acntTag {
		t.Error(acntTag)
		t.Fatal(test_acntTag)
	}
	acntsJt, err := jwt.Parse([]byte(w.HeaderMap.Get("X-Auth-Users")))
	if err != nil {
		t.Fatal(err)
	}
	type account struct {
		Iss   string
		Sub   string
		Email string
	}
	var acntsBuff map[string]*account
	if err := json.Unmarshal(acntsJt.RawBody(), &acntsBuff); err != nil {
		t.Fatal(err)
	} else if len(acntsBuff) == 0 {
		t.Fatal("no accounts")
	} else if subAcnt1 := acntsBuff[test_subAcnt1Tag]; subAcnt1 == nil {
		t.Fatal("no sub account")
	} else if subAcnt1.Iss != idp.Id() {
		t.Error(subAcnt1.Iss)
		t.Fatal(idp.Id())
	} else if subAcnt1.Sub != test_subAcnt1Id {
		t.Error(subAcnt1.Sub)
		t.Fatal(test_subAcnt1Id)
	} else if subAcnt1.Email != test_subAcnt1Email {
		t.Error(subAcnt1.Email)
		t.Fatal(test_subAcnt1Email)
	} else if subAcnt2 := acntsBuff[test_subAcnt2Tag]; subAcnt2 == nil {
		t.Fatal("no sub account")
	} else if subAcnt2.Iss != subIdp.Id() {
		t.Error(subAcnt2.Iss)
		t.Fatal(subIdp.Id())
	} else if subAcnt2.Sub != test_subAcnt2Id {
		t.Error(subAcnt2.Sub)
		t.Fatal(test_subAcnt2Id)
	} else if subAcnt2.Email != test_subAcnt2Email {
		t.Error(subAcnt2.Email)
		t.Fatal(test_subAcnt2Email)
	}
}
// 正常系。
// 元のリクエストパスにリダイレクトさせることの検査。
// X-Auth-User ヘッダに iss, sub, at_tag, at_exp の入った JWT を入れることの検査。
// X-Auth-User ヘッダに追加属性を入れることの検査。
func TestCallback(t *testing.T) {
	// ////////////////////////////////
	// logutil.SetupConsole(logRoot, level.ALL)
	// defer logutil.SetupConsole(logRoot, level.OFF)
	// ////////////////////////////////

	idpServ, err := newTestIdProvider([]jwk.Key{test_idpKey})
	if err != nil {
		t.Fatal(err)
	}
	defer idpServ.close()
	idp := idpServ.info()
	page := newTestPage([]jwk.Key{test_taKey}, []idpdb.Element{idp})

	now := time.Now()
	sess := asession.New(test_sessId, now.Add(page.sessExpIn), test_reqPath, idp.Id(), page.selfId, page.rediUri, test_stat, test_nonc)
	page.sessDb.Save(sess, now.Add(time.Minute))

	r, err := newCallbackRequest(page)
	if err != nil {
		t.Fatal(err)
	}

	s1, h1, b1, err := newTestTokenResponse(page, idp, nil)
	if err != nil {
		t.Fatal(err)
	}
	req1Ch := idpServ.addResponse(s1, h1, b1)
	s2, h2, b2, err := newTestAccountResponse(page, idp)
	if err != nil {
		t.Fatal(err)
	}
	req2Ch := idpServ.addResponse(s2, h2, b2)

	w := httptest.NewRecorder()
	page.HandleCallback(w, r)

	select {
	case req := <-req1Ch:
		if contType, contType2 := "application/x-www-form-urlencoded", req.Header.Get("Content-Type"); contType2 != contType {
			t.Error(contType)
			t.Fatal(contType2)
		} else if grntType, grntType2 := "authorization_code", req.FormValue("grant_type"); grntType2 != grntType {
			t.Error(grntType)
			t.Fatal(grntType2)
		} else if cod := req.FormValue("code"); cod != test_cod {
			t.Error(cod)
			t.Fatal(test_cod)
		} else if rediUri := req.FormValue("redirect_uri"); rediUri != page.rediUri {
			t.Error(rediUri)
			t.Fatal(page.rediUri)
		} else if taId := req.FormValue("client_id"); taId != page.selfId {
			t.Error(taId)
			t.Fatal(page.selfId)
		} else if assType, assType2 := "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", req.FormValue("client_assertion_type"); assType2 != assType {
			t.Error(assType2)
			t.Fatal(assType)
		}
		ass, err := jwt.Parse([]byte(req.FormValue("client_assertion")))
		if err != nil {
			t.Fatal(err)
		} else if !ass.IsSigned() {
			t.Fatal("not signed")
		} else if err := ass.Verify([]jwk.Key{test_taKey}); err != nil {
			t.Fatal(err)
		}
		var buff struct {
			Iss string
			Sub string
			Aud audience.Audience
			Jti string
			Exp int
			Iat int
		}
		if err := json.Unmarshal(ass.RawBody(), &buff); err != nil {
			t.Fatal(err)
		} else if buff.Iss != page.selfId {
			t.Error(buff.Iss)
			t.Fatal(page.selfId)
		} else if buff.Sub != page.selfId {
			t.Error(buff.Sub)
			t.Fatal(page.selfId)
		} else if !buff.Aud[idp.TokenUri()] {
			t.Error(buff.Aud)
			t.Fatal(idp.TokenUri())
		} else if len(buff.Jti) != page.jtiLen {
			t.Error(len(buff.Jti), " "+buff.Jti)
			t.Fatal(page.jtiLen)
		} else if buff.Exp == 0 {
			t.Fatal("no exp")
		} else if buff.Iat == 0 {
			t.Fatal("no iat")
		} else if !(buff.Iat < buff.Exp) {
			t.Error("exp not after iat")
			t.Error(buff.Iat)
			t.Fatal(buff.Exp)
		}
	case <-time.After(time.Minute):
		t.Fatal("no request")
	}

	select {
	case req := <-req2Ch:
		if auth := strings.Fields(req.Header.Get("Authorization")); len(auth) != 2 {
			t.Error("not 2 fields")
			t.Fatal(auth)
		} else if auth[0] != "Bearer" {
			t.Error(auth[0])
			t.Fatal("Bearer")
		} else if auth[1] != test_tok {
			t.Error(auth[1])
			t.Fatal(test_tok)
		}
	case <-time.After(time.Minute):
		t.Fatal("no request")
	}

	if w.Code != http.StatusFound {
		t.Error(w.Code)
		t.Fatal(http.StatusFound)
	} else if uri, err := url.Parse(test_reqPath); err != nil {
		t.Fatal(err)
	} else if uri2, err := url.Parse(w.HeaderMap.Get("Location")); err != nil {
		t.Fatal(err)
	} else if !reflect.DeepEqual(uri2, uri) {
		t.Error(uri2)
		t.Fatal(uri)
	}
	var buff struct {
		Iss    string
		Sub    string
		At_tag string
		At_exp int
		Email  string
	}
	if jt, err := jwt.Parse([]byte(w.HeaderMap.Get("X-Auth-User"))); err != nil {
		t.Fatal(err)
	} else if err := json.Unmarshal(jt.RawBody(), &buff); err != nil {
		t.Fatal(err)
	} else if buff.Iss != idp.Id() {
		t.Error(buff.Iss)
		t.Fatal(idp.Id())
	} else if buff.Sub != test_acntId {
		t.Error(buff.Sub)
		t.Fatal(test_acntId)
	} else if len(buff.At_tag) != page.tokTagLen {
		t.Error(len(buff.At_tag), buff.At_tag)
		t.Fatal(page.tokTagLen)
	} else if buff.At_exp == 0 {
		t.Fatal("no at_exp")
	} else if buff.Email != test_acntEmail {
		t.Error(buff.Email)
		t.Fatal(test_acntEmail)
	}
}
示例#6
0
func testServer(t *testing.T, param *parameters, idpServ *testIdProvider) {
	idp := idpServ.info()

	errCh := make(chan error, 1)
	go func() {
		errCh <- serve(param)
	}()
	defer func() { param.shutCh <- struct{}{} }()

	selfUri, err := url.Parse("http://localhost:" + strconv.Itoa(param.socPort))
	if err != nil {
		t.Fatal(err)
	} else if err := waitServer(selfUri.String()+param.pathOk, errCh, time.Now().Add(time.Minute)); err != nil {
		t.Fatal(err)
	}

	cookJar, err := cookiejar.New(nil)
	if err != nil {
		t.Fatal(err)
	}
	noRedi := func(req *http.Request, via []*http.Request) error { return errors.New("redirect flag") }

	// 認証前リクエスト。
	reqPath := strings.TrimLeft(param.pathAuth, "/") + "/a/b/c"
	authReq, err := newAuthRequest(selfUri.String()+reqPath, idp.AuthUri())
	if err != nil {
		t.Fatal(err)
	}
	authResp, err := (&http.Client{Jar: cookJar, CheckRedirect: noRedi}).Do(authReq)
	if err != nil {
		if e, ok := err.(*url.Error); !ok || e.Err.Error() != "redirect flag" {
			t.Fatal(err)
		}
	}
	defer authResp.Body.Close()
	server.LogResponse(level.DEBUG, authResp, false)

	if authResp.StatusCode != http.StatusFound {
		t.Error(authResp.StatusCode)
		t.Fatal(http.StatusFound)
	}
	authUri, err := url.Parse(authResp.Header.Get("Location"))
	if err != nil {
		t.Fatal(err)
	} else if authUri := authUri.Scheme + "://" + authUri.Host + authUri.Path; authUri != idp.AuthUri() {
		t.Error(authUri)
		t.Fatal(idp.AuthUri())
	}
	sessExist := false
	for _, cook := range cookJar.Cookies(selfUri) {
		if cook.Name == param.asessLabel {
			sessExist = true
			break
		}
	}
	if !sessExist {
		t.Fatal("no new session")
	}
	authQ := authUri.Query()
	if len(authQ) == 0 {
		t.Fatal("no query")
	} else if respType := request.FormValueSet(authQ.Get("response_type")); len(respType) != 1 || !respType["code"] {
		t.Error(respType)
		t.Fatal("code")
	} else if scop := request.FormValueSet(authQ.Get("scope")); len(scop) != 1 || !scop["openid"] {
		t.Error(scop)
		t.Fatal("openid")
	} else if taId := authQ.Get("client_id"); taId != param.selfId {
		t.Error(taId)
		t.Fatal(param.selfId)
	} else if rediUri := authQ.Get("redirect_uri"); rediUri != param.rediUri {
		t.Error(rediUri)
		t.Fatal(param.rediUri)
	} else if authQ.Get("state") == "" {
		t.Fatal("no state")
	} else if authQ.Get("nonce") == "" {
		t.Fatal("no nonce")
	}

	// 認証後リクエスト。
	{
		s, h, b, err := newTestTokenResponse(param.selfId, authQ.Get("nonce"), idp)
		if err != nil {
			t.Fatal(err)
		}
		idpServ.addResponse(s, h, b)
	}
	{
		s, h, b, err := newTestAccountResponse(idp)
		if err != nil {
			t.Fatal(err)
		}
		idpServ.addResponse(s, h, b)
	}

	cbReq, err := newCallbackRequest(selfUri.String()+param.pathCb, authQ.Get("state"))
	if err != nil {
		t.Fatal(err)
	}
	cbResp, err := (&http.Client{Jar: cookJar, CheckRedirect: noRedi}).Do(cbReq)
	if err != nil {
		if e, ok := err.(*url.Error); !ok || e.Err.Error() != "redirect flag" {
			t.Fatal(err)
		}
	}
	defer cbResp.Body.Close()
	server.LogResponse(level.DEBUG, cbResp, false)

	if cbResp.StatusCode != http.StatusFound {
		t.Error(cbResp.StatusCode)
		t.Fatal(http.StatusFound)
	} else if uri, err := url.Parse(cbResp.Header.Get("Location")); err != nil {
		t.Fatal(err)
	} else if uri.Path != reqPath {
		t.Error(uri.Path)
		t.Fatal(reqPath)
	}
	var buff struct {
		Iss    string
		Sub    string
		At_tag string
		At_exp int
		Email  string
	}
	if jt, err := jwt.Parse([]byte(cbResp.Header.Get("X-Auth-User"))); err != nil {
		t.Fatal(err)
	} else if err := json.Unmarshal(jt.RawBody(), &buff); err != nil {
		t.Fatal(err)
	} else if buff.Iss != idp.Id() {
		t.Error(buff.Iss)
		t.Fatal(idp.Id())
	} else if buff.Sub != test_acntId {
		t.Error(buff.Sub)
		t.Fatal(test_acntId)
	} else if len(buff.At_tag) != param.tokTagLen {
		t.Error(len(buff.At_tag), buff.At_tag)
		t.Fatal(param.tokTagLen)
	} else if buff.At_exp == 0 {
		t.Fatal("no at_exp")
	} else if buff.Email != test_acntEmail {
		t.Error(buff.Email)
		t.Fatal(test_acntEmail)
	}
}