-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
-
54
-
55
-
56
-
57
-
58
-
59
-
60
-
61
-
62
-
63
-
64
-
65
-
66
-
67
-
68
-
69
-
70
-
71
-
72
-
73
-
74
-
75
-
76
-
77
-
78
-
79
-
80
-
81
-
82
-
83
package openid
import (
"encoding/xml"
"errors"
"strings"
)
// TODO: As per 11.2 in openid 2 specs, a service may have multiple
// URIs. We don't care for discovery really, but we do care for
// verification though.
type XrdsIdentifier struct {
Type []string `xml:"Type"`
URI string `xml:"URI"`
LocalID string `xml:"LocalID"`
Priority int `xml:"priority,attr"`
}
type Xrd struct {
Service []*XrdsIdentifier `xml:"Service"`
}
type XrdsDocument struct {
XMLName xml.Name `xml:"XRDS"`
Xrd *Xrd `xml:"XRD"`
}
func parseXrds(input []byte) (opEndpoint, opLocalID string, err error) {
xrdsDoc := &XrdsDocument{}
err = xml.Unmarshal(input, xrdsDoc)
if err != nil {
return
}
if xrdsDoc.Xrd == nil {
return "", "", errors.New("XRDS document missing XRD tag")
}
// 7.3.2.2. Extracting Authentication Data
// Once the Relying Party has obtained an XRDS document, it
// MUST first search the document (following the rules
// described in [XRI_Resolution_2.0]) for an OP Identifier
// Element. If none is found, the RP will search for a Claimed
// Identifier Element.
for _, service := range xrdsDoc.Xrd.Service {
// 7.3.2.1.1. OP Identifier Element
// An OP Identifier Element is an <xrd:Service> element with the
// following information:
// An <xrd:Type> tag whose text content is
// "http://specs.openid.net/auth/2.0/server".
// An <xrd:URI> tag whose text content is the OP Endpoint URL
if service.hasType("http://specs.openid.net/auth/2.0/server") {
opEndpoint = strings.TrimSpace(service.URI)
return
}
}
for _, service := range xrdsDoc.Xrd.Service {
// 7.3.2.1.2. Claimed Identifier Element
// A Claimed Identifier Element is an <xrd:Service> element
// with the following information:
// An <xrd:Type> tag whose text content is
// "http://specs.openid.net/auth/2.0/signon".
// An <xrd:URI> tag whose text content is the OP Endpoint
// URL.
// An <xrd:LocalID> tag (optional) whose text content is the
// OP-Local Identifier.
if service.hasType("http://specs.openid.net/auth/2.0/signon") {
opEndpoint = strings.TrimSpace(service.URI)
opLocalID = strings.TrimSpace(service.LocalID)
return
}
}
return "", "", errors.New("Could not find a compatible service")
}
func (xrdsi *XrdsIdentifier) hasType(tpe string) bool {
for _, t := range xrdsi.Type {
if t == tpe {
return true
}
}
return false
}