mirror of
https://github.com/zitadel/zitadel.git
synced 2025-01-07 07:28:57 +00:00
fix: Unrecognized Authentication Type Error when SMTP LOGIN Auth method is required (#7761)
* fix: poc outlook.com now works login auth * fix: remove port arg from smtpAuth * fix: add outlook provider and custom email placeholder * fix: minor typo in contributing docs * fix: use zerrors package * fix: typo for idp and smtp providers --------- Co-authored-by: Max Peintner <max@caos.ch>
This commit is contained in:
parent
2a421a7b8a
commit
1f54f5b8a4
@ -108,13 +108,13 @@ Please make sure you cover your changes with tests before marking a Pull Request
|
|||||||
|
|
||||||
The code consists of the following parts:
|
The code consists of the following parts:
|
||||||
|
|
||||||
| name | description | language | where to find |
|
| name | description | language | where to find |
|
||||||
| --------------- | --------------------------------------------------------------- | --------------------------------------------------------------------------- | -------------------------------------------------- |
|
| --------------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------- | -------------------------------------------------- |
|
||||||
| backend | Service that serves the grpc(-web) and RESTful API | [go](https://go.dev) | [API implementation](./internal/api/grpc) |
|
| backend | Service that serves the grpc(-web) and RESTful API | [go](https://go.dev) | [API implementation](./internal/api/grpc) |
|
||||||
| console | Frontend the user interacts with after log in | [Angular](https://angular.io), [Typescript](https://www.typescriptlang.org) | [./console](./console) |
|
| console | Frontend the user interacts with after log in | [Angular](https://angular.io), [Typescript](https://www.typescriptlang.org) | [./console](./console) |
|
||||||
| login | Server side rendered frontend the user interacts with during login | [go](https://go.dev), [go templates](https://pkg.go.dev/html/template) | [./internal/api/ui/login](./internal/api/ui/login) |
|
| login | Server side rendered frontend the user interacts with during login | [go](https://go.dev), [go templates](https://pkg.go.dev/html/template) | [./internal/api/ui/login](./internal/api/ui/login) |
|
||||||
| API definitions | Specifications of the API | [Protobuf](https://developers.google.com/protocol-buffers) | [./proto/zitadel](./proto/zitadel) |
|
| API definitions | Specifications of the API | [Protobuf](https://developers.google.com/protocol-buffers) | [./proto/zitadel](./proto/zitadel) |
|
||||||
| docs | Project documentation made with docusaurus | [Docusaurus](https://docusaurus.io/) | [./docs](./docs) |
|
| docs | Project documentation made with docusaurus | [Docusaurus](https://docusaurus.io/) | [./docs](./docs) |
|
||||||
|
|
||||||
Please validate and test the code before you contribute.
|
Please validate and test the code before you contribute.
|
||||||
|
|
||||||
@ -129,12 +129,12 @@ We add the label "good first issue" for problems we think are a good starting po
|
|||||||
|
|
||||||
We are committed to creating a welcoming and inclusive community for all developers, regardless of their gender identity or expression. To achieve this, we are actively working to ensure that our contribution guidelines are gender-neutral and use inclusive language.
|
We are committed to creating a welcoming and inclusive community for all developers, regardless of their gender identity or expression. To achieve this, we are actively working to ensure that our contribution guidelines are gender-neutral and use inclusive language.
|
||||||
|
|
||||||
**Use gender-neutral pronouns**:
|
**Use gender-neutral pronouns**:
|
||||||
Don't use gender-specific pronouns unless the person you're referring to is actually that gender.
|
Don't use gender-specific pronouns unless the person you're referring to is actually that gender.
|
||||||
In particular, don't use he, him, his, she, or her as gender-neutral pronouns, and don't use he/she or (s)he or other such punctuational approaches. Instead, use the singular they.
|
In particular, don't use he, him, his, she, or her as gender-neutral pronouns, and don't use he/she or (s)he or other such punctuational approaches. Instead, use the singular they.
|
||||||
|
|
||||||
**Choose gender-neutral alternatives**:
|
**Choose gender-neutral alternatives**:
|
||||||
Opt for gender-neutral terms instead of gendered ones whenever possible.
|
Opt for gender-neutral terms instead of gendered ones whenever possible.
|
||||||
Replace "policeman" with "police officer," "manpower" with "workforce," and "businessman" with "entrepreneur" or "businessperson."
|
Replace "policeman" with "police officer," "manpower" with "workforce," and "businessman" with "entrepreneur" or "businessperson."
|
||||||
|
|
||||||
**Avoid ableist language**:
|
**Avoid ableist language**:
|
||||||
@ -194,7 +194,7 @@ make core_unit_test
|
|||||||
To test the database-connected gRPC API, run PostgreSQL and CockroachDB, set up a ZITADEL instance and run the tests including integration tests:
|
To test the database-connected gRPC API, run PostgreSQL and CockroachDB, set up a ZITADEL instance and run the tests including integration tests:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export INTEGRATION_DB_FLAVOR="postgres" ZITADEL_MASTERKEY="MasterkeyNeedsToHave32Characters"
|
export INTEGRATION_DB_FLAVOR="cockroach" ZITADEL_MASTERKEY="MasterkeyNeedsToHave32Characters"
|
||||||
docker compose -f internal/integration/config/docker-compose.yaml up --pull always --wait ${INTEGRATION_DB_FLAVOR}
|
docker compose -f internal/integration/config/docker-compose.yaml up --pull always --wait ${INTEGRATION_DB_FLAVOR}
|
||||||
make core_integration_test
|
make core_integration_test
|
||||||
docker compose -f internal/integration/config/docker-compose.yaml down
|
docker compose -f internal/integration/config/docker-compose.yaml down
|
||||||
|
@ -53,6 +53,7 @@ export interface ProviderDefaultSettings {
|
|||||||
value: string;
|
value: string;
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
};
|
};
|
||||||
|
senderEmailPlaceholder?: string;
|
||||||
image?: string;
|
image?: string;
|
||||||
routerLinkElement: string;
|
routerLinkElement: string;
|
||||||
}
|
}
|
||||||
@ -102,6 +103,7 @@ export const MailjetDefaultSettings: ProviderDefaultSettings = {
|
|||||||
user: { value: '', placeholder: 'Your Mailjet API key' },
|
user: { value: '', placeholder: 'Your Mailjet API key' },
|
||||||
password: { value: '', placeholder: 'Your Mailjet Secret key' },
|
password: { value: '', placeholder: 'Your Mailjet Secret key' },
|
||||||
image: './assets/images/smtp/mailjet.svg',
|
image: './assets/images/smtp/mailjet.svg',
|
||||||
|
senderEmailPlaceholder: 'An authorized domain or email address',
|
||||||
routerLinkElement: 'mailjet',
|
routerLinkElement: 'mailjet',
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -114,6 +116,7 @@ export const PostmarkDefaultSettings: ProviderDefaultSettings = {
|
|||||||
user: { value: '', placeholder: 'Your Server API token' },
|
user: { value: '', placeholder: 'Your Server API token' },
|
||||||
password: { value: '', placeholder: 'Your Server API token' },
|
password: { value: '', placeholder: 'Your Server API token' },
|
||||||
image: './assets/images/smtp/postmark.png',
|
image: './assets/images/smtp/postmark.png',
|
||||||
|
senderEmailPlaceholder: 'An authorized domain or email address',
|
||||||
routerLinkElement: 'postmark',
|
routerLinkElement: 'postmark',
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -138,6 +141,7 @@ export const MailchimpDefaultSettings: ProviderDefaultSettings = {
|
|||||||
user: { value: '', placeholder: 'Your Mailchimp primary contact email' },
|
user: { value: '', placeholder: 'Your Mailchimp primary contact email' },
|
||||||
password: { value: '', placeholder: 'Your Mailchimp Transactional API key' },
|
password: { value: '', placeholder: 'Your Mailchimp Transactional API key' },
|
||||||
image: './assets/images/smtp/mailchimp.svg',
|
image: './assets/images/smtp/mailchimp.svg',
|
||||||
|
senderEmailPlaceholder: 'An authorized domain or email address',
|
||||||
routerLinkElement: 'mailchimp',
|
routerLinkElement: 'mailchimp',
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -153,6 +157,19 @@ export const BrevoDefaultSettings: ProviderDefaultSettings = {
|
|||||||
routerLinkElement: 'brevo',
|
routerLinkElement: 'brevo',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const OutlookDefaultSettings: ProviderDefaultSettings = {
|
||||||
|
name: 'outlook.com',
|
||||||
|
requiredTls: true,
|
||||||
|
host: 'smtp-mail.outlook.com',
|
||||||
|
unencryptedPort: 587,
|
||||||
|
encryptedPort: 587,
|
||||||
|
user: { value: '', placeholder: 'Your outlook.com email address' },
|
||||||
|
password: { value: '', placeholder: 'Your outlook.com password' },
|
||||||
|
image: './assets/images/smtp/outlook.svg',
|
||||||
|
senderEmailPlaceholder: 'Your outlook.com email address',
|
||||||
|
routerLinkElement: 'outlook',
|
||||||
|
};
|
||||||
|
|
||||||
export const GenericDefaultSettings: ProviderDefaultSettings = {
|
export const GenericDefaultSettings: ProviderDefaultSettings = {
|
||||||
name: 'generic',
|
name: 'generic',
|
||||||
requiredTls: false,
|
requiredTls: false,
|
||||||
@ -170,5 +187,6 @@ export const SMTPKnownProviders = [
|
|||||||
MailjetDefaultSettings,
|
MailjetDefaultSettings,
|
||||||
PostmarkDefaultSettings,
|
PostmarkDefaultSettings,
|
||||||
SendgridDefaultSettings,
|
SendgridDefaultSettings,
|
||||||
|
OutlookDefaultSettings,
|
||||||
GenericDefaultSettings,
|
GenericDefaultSettings,
|
||||||
];
|
];
|
||||||
|
@ -13,6 +13,7 @@ const types = [
|
|||||||
{ path: 'mailjet', component: SMTPProviderComponent },
|
{ path: 'mailjet', component: SMTPProviderComponent },
|
||||||
{ path: 'mailchimp', component: SMTPProviderComponent },
|
{ path: 'mailchimp', component: SMTPProviderComponent },
|
||||||
{ path: 'brevo', component: SMTPProviderComponent },
|
{ path: 'brevo', component: SMTPProviderComponent },
|
||||||
|
{ path: 'outlook', component: SMTPProviderComponent },
|
||||||
];
|
];
|
||||||
|
|
||||||
const routes: Routes = types.map((value) => {
|
const routes: Routes = types.map((value) => {
|
||||||
|
@ -106,7 +106,13 @@
|
|||||||
|
|
||||||
<cnsl-form-field class="smtp-form-field" label="Sender Address">
|
<cnsl-form-field class="smtp-form-field" label="Sender Address">
|
||||||
<cnsl-label>{{ 'SETTING.SMTP.SENDERADDRESS' | translate }}</cnsl-label>
|
<cnsl-label>{{ 'SETTING.SMTP.SENDERADDRESS' | translate }}</cnsl-label>
|
||||||
<input cnslInput name="senderAddress" formControlName="senderAddress" placeholder="sender@example.com" required />
|
<input
|
||||||
|
cnslInput
|
||||||
|
name="senderAddress"
|
||||||
|
formControlName="senderAddress"
|
||||||
|
placeholder="{{ senderEmailPlaceholder }}"
|
||||||
|
required
|
||||||
|
/>
|
||||||
</cnsl-form-field>
|
</cnsl-form-field>
|
||||||
|
|
||||||
<cnsl-form-field class="smtp-form-field" label="Sender Name">
|
<cnsl-form-field class="smtp-form-field" label="Sender Name">
|
||||||
|
@ -28,6 +28,7 @@ import {
|
|||||||
MailjetDefaultSettings,
|
MailjetDefaultSettings,
|
||||||
PostmarkDefaultSettings,
|
PostmarkDefaultSettings,
|
||||||
ProviderDefaultSettings,
|
ProviderDefaultSettings,
|
||||||
|
OutlookDefaultSettings,
|
||||||
SendgridDefaultSettings,
|
SendgridDefaultSettings,
|
||||||
} from './known-smtp-providers-settings';
|
} from './known-smtp-providers-settings';
|
||||||
|
|
||||||
@ -56,6 +57,8 @@ export class SMTPProviderComponent {
|
|||||||
public firstFormGroup!: UntypedFormGroup;
|
public firstFormGroup!: UntypedFormGroup;
|
||||||
public secondFormGroup!: UntypedFormGroup;
|
public secondFormGroup!: UntypedFormGroup;
|
||||||
|
|
||||||
|
public senderEmailPlaceholder = 'sender@example.com';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private service: AdminService,
|
private service: AdminService,
|
||||||
private _location: Location,
|
private _location: Location,
|
||||||
@ -91,6 +94,9 @@ export class SMTPProviderComponent {
|
|||||||
case 'brevo':
|
case 'brevo':
|
||||||
this.providerDefaultSetting = BrevoDefaultSettings;
|
this.providerDefaultSetting = BrevoDefaultSettings;
|
||||||
break;
|
break;
|
||||||
|
case 'outlook':
|
||||||
|
this.providerDefaultSetting = OutlookDefaultSettings;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.firstFormGroup = this.fb.group({
|
this.firstFormGroup = this.fb.group({
|
||||||
@ -106,6 +112,8 @@ export class SMTPProviderComponent {
|
|||||||
password: [this.providerDefaultSetting?.password.value || ''],
|
password: [this.providerDefaultSetting?.password.value || ''],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.senderEmailPlaceholder = this.providerDefaultSetting?.senderEmailPlaceholder || 'sender@example.com';
|
||||||
|
|
||||||
this.secondFormGroup = this.fb.group({
|
this.secondFormGroup = this.fb.group({
|
||||||
senderAddress: ['', [requiredValidator]],
|
senderAddress: ['', [requiredValidator]],
|
||||||
senderName: ['', [requiredValidator]],
|
senderName: ['', [requiredValidator]],
|
||||||
|
@ -11,7 +11,8 @@
|
|||||||
'/instance/smtpprovider/postmark/create',
|
'/instance/smtpprovider/postmark/create',
|
||||||
'/instance/smtpprovider/sendgrid/create',
|
'/instance/smtpprovider/sendgrid/create',
|
||||||
'/instance/smtpprovider/mailchimp/create',
|
'/instance/smtpprovider/mailchimp/create',
|
||||||
'/instance/smtpprovider/brevo/create'
|
'/instance/smtpprovider/brevo/create',
|
||||||
|
'/instance/smtpprovider/outlook/create'
|
||||||
]"
|
]"
|
||||||
[timestamp]="configsResult?.details?.viewTimestamp"
|
[timestamp]="configsResult?.details?.viewTimestamp"
|
||||||
[selection]="selection"
|
[selection]="selection"
|
||||||
|
@ -1994,7 +1994,7 @@
|
|||||||
},
|
},
|
||||||
"CREATE": {
|
"CREATE": {
|
||||||
"TITLE": "Add provider",
|
"TITLE": "Add provider",
|
||||||
"DESCRIPTION": "Select one ore more of the following providers.",
|
"DESCRIPTION": "Select one or more of the following providers.",
|
||||||
"STEPPERTITLE": "Create Provider",
|
"STEPPERTITLE": "Create Provider",
|
||||||
"OIDC": {
|
"OIDC": {
|
||||||
"TITLE": "OIDC Provider",
|
"TITLE": "OIDC Provider",
|
||||||
@ -2264,7 +2264,7 @@
|
|||||||
},
|
},
|
||||||
"CREATE": {
|
"CREATE": {
|
||||||
"TITLE": "Add SMTP provider",
|
"TITLE": "Add SMTP provider",
|
||||||
"DESCRIPTION": "Select one ore more of the following providers.",
|
"DESCRIPTION": "Select one or more of the following providers.",
|
||||||
"STEPS": {
|
"STEPS": {
|
||||||
"TITLE": "Add {{ value }} SMTP Provider",
|
"TITLE": "Add {{ value }} SMTP Provider",
|
||||||
"CREATE_DESC_TITLE": "Enter your {{ value }} SMTP settings step by step",
|
"CREATE_DESC_TITLE": "Enter your {{ value }} SMTP settings step by step",
|
||||||
|
1
console/src/assets/images/smtp/outlook.svg
Normal file
1
console/src/assets/images/smtp/outlook.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 5.2 KiB |
@ -152,10 +152,7 @@ func (smtpConfig SMTP) smtpAuth(client *smtp.Client, host string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Auth
|
// Auth
|
||||||
auth := unencryptedAuth{
|
err := client.Auth(PlainOrLoginAuth(smtpConfig.User, smtpConfig.Password, host))
|
||||||
smtp.PlainAuth("", smtpConfig.User, smtpConfig.Password, host),
|
|
||||||
}
|
|
||||||
err := client.Auth(auth)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return zerrors.ThrowInternalf(err, "EMAIL-s9kfs", "could not add smtp auth for user %s", smtpConfig.User)
|
return zerrors.ThrowInternalf(err, "EMAIL-s9kfs", "could not add smtp auth for user %s", smtpConfig.User)
|
||||||
}
|
}
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
package smtp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/smtp"
|
|
||||||
)
|
|
||||||
|
|
||||||
type unencryptedAuth struct {
|
|
||||||
smtp.Auth
|
|
||||||
}
|
|
||||||
|
|
||||||
// PlainAuth returns an Auth that implements the PLAIN authentication
|
|
||||||
// mechanism as defined in RFC 4616. The returned Auth uses the given
|
|
||||||
// username and password to authenticate to host and act as identity.
|
|
||||||
// Usually identity should be the empty string, to act as username.
|
|
||||||
//
|
|
||||||
// This reimplementation allows it to work over non-TLS connections
|
|
||||||
|
|
||||||
func (a unencryptedAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
|
|
||||||
s := *server
|
|
||||||
s.TLS = true
|
|
||||||
return a.Auth.Start(&s)
|
|
||||||
}
|
|
57
internal/notification/channels/smtp/plain_or_login_auth.go
Normal file
57
internal/notification/channels/smtp/plain_or_login_auth.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package smtp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"net/smtp"
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// golang net/smtp SMTP AUTH LOGIN or PLAIN Auth Handler
|
||||||
|
// Reference: https://gist.github.com/andelf/5118732?permalink_comment_id=4825669#gistcomment-4825669
|
||||||
|
|
||||||
|
func PlainOrLoginAuth(username, password, host string) smtp.Auth {
|
||||||
|
return &plainOrLoginAuth{username: username, password: password, host: host}
|
||||||
|
}
|
||||||
|
|
||||||
|
type plainOrLoginAuth struct {
|
||||||
|
username string
|
||||||
|
password string
|
||||||
|
host string
|
||||||
|
authMethod string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *plainOrLoginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
|
||||||
|
if server.Name != a.host {
|
||||||
|
return "", nil, zerrors.ThrowInternal(nil, "SMTP-RRi75", "wrong host name")
|
||||||
|
}
|
||||||
|
if !slices.Contains(server.Auth, "PLAIN") {
|
||||||
|
a.authMethod = "LOGIN"
|
||||||
|
return a.authMethod, nil, nil
|
||||||
|
} else {
|
||||||
|
a.authMethod = "PLAIN"
|
||||||
|
resp := []byte("\x00" + a.username + "\x00" + a.password)
|
||||||
|
return a.authMethod, resp, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *plainOrLoginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
|
||||||
|
if !more {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.authMethod == "PLAIN" {
|
||||||
|
// We've already sent everything.
|
||||||
|
return nil, zerrors.ThrowInternal(nil, "SMTP-AAf43", "unexpected server challenge for PLAIN auth method")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case bytes.Equal(fromServer, []byte("Username:")):
|
||||||
|
return []byte(a.username), nil
|
||||||
|
case bytes.Equal(fromServer, []byte("Password:")):
|
||||||
|
return []byte(a.password), nil
|
||||||
|
default:
|
||||||
|
return nil, zerrors.ThrowInternal(nil, "SMTP-HjW21", "unexpected server challenge")
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user