Files
pocketbase/tools/auth/mailcow.go
2026-05-03 23:22:40 +02:00

87 lines
6.0 KiB
Go

package auth
import (
"context"
"encoding/json"
"errors"
"strings"
"github.com/tabshift-gh/pocketbase/tools/types"
"golang.org/x/oauth2"
)
func init() {
Providers[NameMailcow] = wrapFactory(NewMailcowProvider)
}
var _ Provider = (*Mailcow)(nil)
// NameMailcow is the unique name of the mailcow provider.
const NameMailcow string = "mailcow"
// Mailcow allows authentication via mailcow OAuth2.
type Mailcow struct {
BaseProvider
}
// NewMailcowProvider creates a new mailcow provider instance with some defaults.
func NewMailcowProvider() *Mailcow {
return &Mailcow{BaseProvider{
ctx: context.Background(),
order: 28,
logo: `<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="394.8" height="376.9"><path d="M56 213.3c0-20.3-.8-17.3-3.7-39.3 2.2-22.5-7.6-38.9-7.7-58.4 0-4.7-5.8-6.7-4.7-11A99 99 0 0 0 56 214.7z" style="fill:#3d5263" transform="translate(10 10)"/><path d="m254.8 180.4-.6.5a121 121 0 0 1-80 84l11 9.2 53.2 44.8 31.6 26.5q1.3-3 1.3-6.4V167.1zM23 185.5l-6.5-5.2-16.5-13v171.6q0 3.5 1.2 6.5l58.4-48.7 27-22.4L99 263.9a121 121 0 0 1-76-78.4" style="fill:#f9e82d" transform="translate(10 10)"/><path d="M238.4 318.9 185.1 274l-11-9.2a120 120 0 0 1-75.1-1l-12.4 10.4-27 22.5-58.4 48.6A18 18 0 0 0 18 357h235.4a18 18 0 0 0 16.7-11.5z" style="fill:#edd514;fill-opacity:.89499996" transform="translate(10 10)"/><path d="M238.4 318.9c-41.4 4-115 5.5-178.8-22.1-21.5-9.4-42-22.1-59.6-39.2V339q0 3.5 1.2 6.5A18 18 0 0 0 18 357h235.4a18 18 0 0 0 18-18v-24.5s-12.6 2.5-32.9 4.5" style="opacity:.1;fill:#3d5263" transform="translate(10 10)"/><path d="M86.6 274.3a120 120 0 0 0 98.5-.2 120 120 0 0 0 69.7-93.7l-.6.5a120.5 120.5 0 0 1-155.2 83 121 121 0 0 1-76-78.4l-6.5-5.2c5.5 42 32.7 77.3 70 94" style="opacity:.1;fill:#3d5263" transform="translate(10 10)"/><path d="M54.3 63.9q-2.7 2.6-5.2 5.4l-.2.1-.7.8.2-.2a120.2 120.2 0 0 1 174.8 165 120 120 0 0 0 35-84.8A120 120 0 0 0 187 40.4a119 119 0 0 0-100.7 1.2 120 120 0 0 0-32 22.3" style="fill:#fff" transform="translate(10 10)"/><path d="M95.8 118.5a4.6 4.6 0 1 0 0-9.2 4.6 4.6 0 0 0 0 9.2m91.1 0a4.6 4.6 0 1 0 0-9.2 4.6 4.6 0 0 0 0 9.2" style="fill:#fff" transform="translate(10 10)"/><path d="M223.7 234.4A120.2 120.2 0 0 0 48.4 70 121 121 0 0 0 23 185.5 120.5 120.5 0 0 0 174.2 265a120 120 0 0 0 47.7-28.6zm-5.8-58.4q-3.2 11-8.7 20.5A94.8 94.8 0 0 1 56 214.8a99 99 0 0 1-16-110.3 96 96 0 0 1 97.8-54 97.3 97.3 0 0 1 84.3 97.2q0 14.9-4 28.3M49 69.3" style="fill:#f1f2f2" transform="translate(10 10)"/><path d="M257.6 161.9a120 120 0 0 1-3.4 19l.6-.5 16.5-13.2zM0 167.2l16.5 13.1 6.5 5.2q-3.5-11.6-4.7-23.9l-2.9.9zm87.5 25.1a11.3 11.3 0 1 0 0 22.5 11.3 11.3 0 0 0 0-22.5m93.8 0a11.3 11.3 0 1 0 0 22.5 11.3 11.3 0 0 0 0-22.5m1.7-90c-7 0-15.4 7.7-15.4 14.7s8 17.2 15 17.2 15.8-9.5 15.8-16.5-8.4-15.4-15.4-15.4m3.9 16.2a4.6 4.6 0 1 1 0-9.2 4.6 4.6 0 0 1 0 9.2m-97.2-15.9c-7 0-14.4 8.1-14.4 15s8.9 16.2 15.8 16.2 15.8-9.1 15.8-16.1-10.2-15.1-17.2-15.1m6.1 16a4.6 4.6 0 1 1 0-9.3 4.6 4.6 0 0 1 0 9.2" style="fill:#5a3620" transform="translate(10 10)"/><path d="M336.3 256.4c3.6-9.1 7.7-11 9.3-11.3-40.7 3.7-36.6 27.7-34 36l1 2.7s2 4.8 7.6 8.7c4.1 3 10.3 5.5 19 5.2 27 .4 35.6-50.2 35.6-50.2-6.6 11.7-26.4 9.9-38.5 9M49 69.4l5.3-5.4c-9-7-24.4-15.9-41.8-11.7A54 54 0 0 0 36 86.5q5.5-8.8 12.4-16.5l-.2.2zm209.8-17.2a49 49 0 0 0-39.2 9.8q10.8 9.9 19 22.3a54 54 0 0 0 20.2-32M134.3 160.2c-43.3 0-78.4 23-78.4 51.3v1.7l.2 1.6a94.8 94.8 0 0 0 153.1-18.3c-9.8-21-39.6-36.3-75-36.3M87.5 215a11.3 11.3 0 1 1 0-22.6 11.3 11.3 0 0 1 0 22.5m93.8 0a11.3 11.3 0 1 1 0-22.6 11.3 11.3 0 0 1 0 22.5M86.3 0c-18.2 16.4-.2 41.4 0 41.6Q102.6 34 121 31.1C97.6 27.7 86.3 0 86.3 0m99.9 0S175.3 26.5 153 30.9q18 2.3 34 9.5c3.4-5.3 15-26.1-.8-40.4" style="fill:#fef3df" transform="translate(10 10)"/><path d="M218 176a163 163 0 0 0 6.5-35.7c0-50-39.3-83.9-86.8-89.8-2.1 28 3.7 87 80.2 125.5m-47.6-58.3a12.6 12.6 0 1 1 25.2 0 12.6 12.6 0 0 1-25.2 0" style="fill:#87654a" transform="translate(10 10)"/><path d="m312.7 283.8-1-2.8a54 54 0 0 1-27.1 12.5q-6 1-13.3.5v28a206 206 0 0 0 26.2-12.5h.1c8.2-4.7 16.4-10.5 22.6-17-5.5-4-7.5-8.8-7.5-8.8M12.5 52.2C30 48 45.4 57 54.3 64q8.4-8.2 18.3-14.6C48.3 18.5 2.2 37.2 2.2 37.2A55 55 0 0 0 31.7 94q2-3.7 4.3-7.5C15.4 72.7 12.5 52.2 12.5 52.2m187.9-4.8q10.3 6.3 19.2 14.6a49 49 0 0 1 39.2-9.8s-2.5 18.4-20.3 32q2.5 3.8 4.6 7.7a54 54 0 0 0 26-54.7s-44-18-68.7 10.2m-61.5 3.1a96 96 0 0 0-99 54q-1.5 6.6-1.6 13.6v4.2q.3 6 1 11.1c4.2 25 7.9 42.5 13.4 66.8l.2 1.2q1 6 3 11.8v-1.7c0-28.3 35-51.3 78.4-51.3 35.3 0 65.1 15.3 75 36.3a99 99 0 0 0 8.6-20.5c-38-23.5-53.9-41.1-64.6-64.2-10.8-23-15.5-47.3-14.4-61.3M91 130.3a12.6 12.6 0 1 1 0-25.2 12.6 12.6 0 0 1 0 25.2" style="fill:#b58765" transform="translate(10 10)"/></svg>`,
displayName: "mailcow",
pkce: true,
scopes: []string{"profile"},
}}
}
// FetchAuthUser returns an AuthUser instance based on mailcow's user api.
//
// API reference: https://github.com/mailcow/mailcow-dockerized/blob/master/data/web/oauth/profile.php
func (p *Mailcow) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
data, err := p.FetchRawUserInfo(token)
if err != nil {
return nil, err
}
rawUser := map[string]any{}
if err := json.Unmarshal(data, &rawUser); err != nil {
return nil, err
}
extracted := struct {
Id string `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
FullName string `json:"full_name"`
Active int `json:"active"`
}{}
if err := json.Unmarshal(data, &extracted); err != nil {
return nil, err
}
if extracted.Active != 1 {
return nil, errors.New("the mailcow user is not active")
}
user := &AuthUser{
Id: extracted.Id,
Name: extracted.FullName,
Username: extracted.Username,
Email: extracted.Email,
RawUser: rawUser,
AccessToken: token.AccessToken,
RefreshToken: token.RefreshToken,
}
user.Expiry, _ = types.ParseDateTime(token.Expiry)
// mailcow usernames are usually just the email adresses, so we just take the part in front of the @
if strings.Contains(user.Username, "@") {
user.Username = strings.Split(user.Username, "@")[0]
}
return user, nil
}