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 element with the // following information: // An tag whose text content is // "http://specs.openid.net/auth/2.0/server". // An 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 element // with the following information: // An tag whose text content is // "http://specs.openid.net/auth/2.0/signon". // An tag whose text content is the OP Endpoint // URL. // An 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 }